Rescues cached entries from jar files that are the same, save for the timestamp.
Also updates the key value used for the cache to include the prefix for RerootedResource
instances.

Review at http://gwt-code-reviews.appspot.com/1441803


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10180 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
index fbf73d1..6cb542b 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
@@ -30,17 +30,46 @@
   private final Collection<CompiledClass> compiledClasses;
   private final ContentId contentId;
   private final Dependencies dependencies;
-  private final String resourceLocation;
-  private final String resourcePath;
-  private final List<JsniMethod> jsniMethods;
-  private final long lastModified;
-  private final MethodArgNamesLookup methodArgNamesLookup;
-  private final String typeName;
   private final boolean isError;
   private final boolean isGenerated;
   private final boolean isSuperSource;
+  private final List<JsniMethod> jsniMethods;
+  private final long lastModified;
+  private final MethodArgNamesLookup methodArgNamesLookup;
   private final CategorizedProblem[] problems;
+  private final String resourceLocation;
+  private final String resourcePath;
   private final DiskCacheToken sourceToken;
+  private final String typeName;
+
+  /**
+   * Shallow copy of a CachedCompiliationUnit, replacing some parameters in the new copy.
+   *
+   * @param unit Unit to clone.
+   * @param lastModified last modified date to replace in the clone
+   * @param resourceLocation location to replace in the clone.
+   */
+  public CachedCompilationUnit(CachedCompilationUnit unit, long lastModified,
+      String resourceLocation) {
+    assert unit != null;
+    this.compiledClasses = unit.getCompiledClasses();
+    this.contentId = unit.getContentId();
+    this.dependencies = unit.getDependencies();
+    this.resourcePath = unit.getResourcePath();
+    this.jsniMethods = unit.getJsniMethods();
+    this.methodArgNamesLookup = unit.getMethodArgs();
+    this.typeName = unit.getTypeName();
+    this.isError = unit.isError();
+    this.isGenerated = unit.isGenerated();
+    this.isSuperSource = unit.isSuperSource();
+    this.problems = unit.problems;
+    this.astToken = unit.astToken;
+    this.sourceToken = unit.sourceToken;
+
+    // Override these fields
+    this.lastModified = lastModified;
+    this.resourceLocation = resourceLocation;
+  }
 
   /**
    * Create a compilation unit that can be serialized from another
@@ -81,6 +110,11 @@
   }
 
   @Override
+  public CachedCompilationUnit asCachedCompilationUnit() {
+    return this;
+  }
+
+  @Override
   public Collection<CompiledClass> getCompiledClasses() {
     return compiledClasses;
   }
@@ -144,11 +178,6 @@
   }
 
   @Override
-  protected Object writeReplace() {
-    return this;
-  }
-
-  @Override
   ContentId getContentId() {
     return contentId;
   }
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
index 2605eef..498f003 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2009 Google Inc.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -371,7 +371,7 @@
   /**
    * Build a new compilation state from a source oracle. Allow the caller to
    * specify a compiler delegate that will handle undefined names.
-   * 
+   *
    * TODO: maybe use a finer brush than to synchronize the whole thing.
    */
   public synchronized CompilationState doBuildFrom(TreeLogger logger, Set<Resource> resources,
@@ -393,14 +393,24 @@
       ResourceCompilationUnitBuilder builder =
           new ResourceCompilationUnitBuilder(typeName, resource);
 
-      CompilationUnit cachedUnit = unitCache.find(resource.getPath());
-      if (cachedUnit != null) {
-        if (cachedUnit.getLastModified() == resource.getLastModified()) {
-          cachedUnits.put(builder, cachedUnit);
-          compileMoreLater.addValidUnit(cachedUnit);
-          continue;
-        }
+      CompilationUnit cachedUnit = unitCache.find(resource.getPathPrefix() + resource.getPath());
+      if (cachedUnit != null && cachedUnit.getLastModified() != resource.getLastModified()) {
         unitCache.remove(cachedUnit);
+        if (!cachedUnit.getContentId().equals(builder.getContentId())) {
+          cachedUnit = null;
+        } else {
+          // Update the cache. The location might have changed since last build
+          // (e.g. jar to file)
+          CachedCompilationUnit updatedUnit =
+              new CachedCompilationUnit(cachedUnit.asCachedCompilationUnit(),
+                  resource.getLastModified(), resource.getLocation());
+          unitCache.add(updatedUnit);
+        }
+      }
+      if (cachedUnit != null) {
+        cachedUnits.put(builder, cachedUnit);
+        compileMoreLater.addValidUnit(cachedUnit);
+        continue;
       }
       builders.add(builder);
     }
@@ -422,7 +432,7 @@
 
   /**
    * Compile new generated units into an existing state.
-   * 
+   *
    * TODO: maybe use a finer brush than to synchronize the whole thing.
    */
   synchronized Collection<CompilationUnit> doBuildGeneratedTypes(TreeLogger logger,
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
index 16ccd78..e9c8881 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
@@ -209,6 +209,12 @@
    */
   private transient Map<String, String> anonymousClassMap = null;
 
+  /**
+   * Returns the unit as an instance of {@link CachedCompilationUnit}, making
+   * a copy if necessary.
+   */
+  public abstract CachedCompilationUnit asCachedCompilationUnit();
+
   @Deprecated
   public final boolean constructAnonymousClassMappings(TreeLogger logger) {
     /*
@@ -292,9 +298,11 @@
   public abstract String getResourceLocation();
 
   /**
-   * Returns the full abstract path of the resource.
+   * Returns the full abstract path of the resource. If a resource has been
+   * re-rooted, this path should include any path prefix that was stripped.
    * 
-   * @see {@link com.google.gwt.dev.resource.Resource#getPath()}
+   * @see {@link com.google.gwt.dev.resource.Resource#getPath()} and
+   *      {@link com.google.gwt.dev.resource.Resource#getPathPrefix()}
    */
   public abstract String getResourcePath();
 
@@ -376,10 +384,12 @@
   }
 
   /**
-   * Subclasses must implement explicit serialization. The canonical serialized
-   * form is {@link CachedCompilationUnit}.
+   * The canonical serialized form of a CompilatinUnit is
+   * {@link CachedCompilationUnit}.
    */
-  protected abstract Object writeReplace();
+  protected final Object writeReplace() {
+    return asCachedCompilationUnit();
+  }
 
   /**
    * Returns the content ID for the source with which this unit was compiled.
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
index e8d7ad3..48986cd 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2009 Google Inc.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -168,6 +168,13 @@
     }
 
     @Override
+    public CachedCompilationUnit asCachedCompilationUnit() {
+      long sourceToken = generatedUnit.getSourceToken();
+      assert sourceToken >= 0;
+      return new CachedCompilationUnit(this, sourceToken, astToken);
+    }
+
+    @Override
     public long getLastModified() {
       return generatedUnit.creationTime();
     }
@@ -206,13 +213,6 @@
     }
 
     @Override
-    protected Object writeReplace() {
-      long sourceToken = generatedUnit.getSourceToken();
-      assert sourceToken >= 0;
-      return new CachedCompilationUnit(this, sourceToken, astToken);
-    }
-
-    @Override
     ContentId getContentId() {
       return new ContentId(getTypeName(), generatedUnit.getStrongHash());
     }
diff --git a/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
index 1ee51e1..3ba90eb 100644
--- a/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2008 Google Inc.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -54,6 +54,14 @@
   }
 
   @Override
+  public CachedCompilationUnit asCachedCompilationUnit() {
+    if (sourceToken < 0) {
+      sourceToken = diskCache.transferFromStream(sourceFile.openContents());
+    }
+    return new CachedCompilationUnit(this, sourceToken, astToken);
+  }
+
+  @Override
   public long getLastModified() {
     return lastModified;
   }
@@ -65,7 +73,7 @@
 
   @Override
   public String getResourcePath() {
-    return sourceFile.getPath();
+    return sourceFile.getPathPrefix() + sourceFile.getPath();
   }
 
   @Deprecated
@@ -102,14 +110,6 @@
   }
 
   @Override
-  protected Object writeReplace() {
-    if (sourceToken < 0) {
-      sourceToken = diskCache.transferFromStream(sourceFile.openContents());
-    }
-    return new CachedCompilationUnit(this, sourceToken, astToken);
-  }
-
-  @Override
   ContentId getContentId() {
     return contentId;
   }
diff --git a/dev/core/src/com/google/gwt/dev/javac/UnitCache.java b/dev/core/src/com/google/gwt/dev/javac/UnitCache.java
index c25071e..ae5fec2 100644
--- a/dev/core/src/com/google/gwt/dev/javac/UnitCache.java
+++ b/dev/core/src/com/google/gwt/dev/javac/UnitCache.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2011 Google Inc.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -40,8 +40,9 @@
   CompilationUnit find(ContentId contentId);
 
   /**
-   * Lookup a {@link CompilationUnit} by resource path.
-   * 
+   * Lookup a {@link CompilationUnit} by resource path. This should include any
+   * path prefix that may have been was stripped to reroot the resource.
+   *
    * @see {@link CompilationUnit#getResourcePath()}
    */
   CompilationUnit find(String resourcePath);
diff --git a/dev/core/src/com/google/gwt/dev/resource/Resource.java b/dev/core/src/com/google/gwt/dev/resource/Resource.java
index cd91d9b..93926b4 100644
--- a/dev/core/src/com/google/gwt/dev/resource/Resource.java
+++ b/dev/core/src/com/google/gwt/dev/resource/Resource.java
@@ -50,6 +50,14 @@
   public abstract String getPath();
 
   /**
+   * If some prefix was stripped from the path, as is for RerootedResources,
+   * retrieve it back with this method.
+   */
+  public String getPathPrefix() {
+    return "";
+  }
+
+  /**
    * Returns a URL for this resource if the resource happens to be based off the
    * file system, otherwise returns <code>null</code>.
    * 
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
index 0ca45bb..172a981 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
@@ -95,6 +95,12 @@
     }
 
     @Override
+    public String getPathPrefix() {
+      int fullPathLen = resource.getPath().length();
+      return resource.getPath().substring(0, fullPathLen - path.length());
+    }
+
+    @Override
     public InputStream openContents() {
       return resource.openContents();
     }
diff --git a/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java b/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java
index eff8fa9..2b16917 100644
--- a/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java
+++ b/dev/core/test/com/google/gwt/dev/javac/MockCompilationUnit.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2011 Google Inc.
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -15,6 +15,9 @@
  */
 package com.google.gwt.dev.javac;
 
+import com.google.gwt.dev.util.DiskCache;
+import com.google.gwt.dev.util.Util;
+
 import org.eclipse.jdt.core.compiler.CategorizedProblem;
 
 import java.util.Collection;
@@ -46,6 +49,14 @@
   }
 
   @Override
+  public CachedCompilationUnit asCachedCompilationUnit() {
+    DiskCache diskCache = DiskCache.INSTANCE;
+    long sourceToken = diskCache.writeByteArray(Util.getBytes(source));
+    long astToken = diskCache.writeByteArray(Util.getBytes("Dummy AST data"));
+    return new CachedCompilationUnit(this, sourceToken, astToken);
+  }
+
+  @Override
   public Collection<CompiledClass> getCompiledClasses() {
     return null;
   }
@@ -105,10 +116,6 @@
     return false;
   }
 
-  protected Object writeReplace() {
-    return this;
-  }
-
   @Override
   ContentId getContentId() {
     return contentId;