Simpler ResourceOracleImpl.
Review by: amitmanjhi
git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@4534 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefix.java b/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefix.java
index c844d86..096dfb1 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefix.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefix.java
@@ -33,6 +33,7 @@
private final ResourceFilter filter;
private final String prefix;
+ private int priority = -1;
private final boolean shouldReroot;
/**
@@ -140,6 +141,15 @@
return prefix + (shouldReroot ? "**" : "*") + (filter == null ? "" : "?");
}
+ int getPriority() {
+ return priority;
+ }
+
+ void setPriority(int priority) {
+ assert (this.priority == -1);
+ this.priority = priority;
+ }
+
private void assertValidPrefix(String prefix) {
assert (prefix != null);
assert ("".equals(prefix) || (!prefix.startsWith("/") && prefix.endsWith("/"))) : "malformed prefix";
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java b/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java
index cbb2a9a..0220db6 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java
@@ -15,9 +15,11 @@
*/
package com.google.gwt.dev.resource.impl;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -91,15 +93,11 @@
}
/**
- * Map of pathPrefix => the sequence number when the pathPrefix was added.
+ * List of all path prefixes in priority order.
*/
- private final Map<PathPrefix, Integer> prefixes = new HashMap<PathPrefix, Integer>();
+ private final List<PathPrefix> prefixes = new ArrayList<PathPrefix>();
private final TrieNode rootTrieNode = new TrieNode("/");
- /**
- * The sequence number in which the PathPrefix was added.
- */
- private int size = 0;
/**
* @param prefix the prefix to add
@@ -109,13 +107,14 @@
* wins)
*/
public boolean add(PathPrefix prefix) {
- ++size;
- prefixes.put(prefix, size);
+ prefix.setPriority(prefixes.size());
+ prefixes.add(prefix);
+
String pathPrefix = prefix.getPrefix();
/*
- * An empty prefix means we have no prefix requirement, but we do attached
- * the prefix to the root so that we can apply the filter.
+ * An empty prefix means we have no prefix requirement, but we do attach the
+ * prefix to the root so that we can apply the filter.
*/
if ("".equals(pathPrefix)) {
rootTrieNode.setPathPrefix(prefix);
@@ -144,7 +143,7 @@
}
public int getSize() {
- return size;
+ return prefixes.size();
}
/**
@@ -262,30 +261,13 @@
return mostSpecificPrefix;
}
- /**
- * Returns true if the first pathPrefix is inserted into the PathPrefixSet
- * after the second pathPrefix. Also, rereooting PathPrefixes take priority
- * over non-rerooting ones (ie, super-source).
- */
- public boolean secondPrefixOverridesFirst(PathPrefix prefix1,
- PathPrefix prefix2) {
- if (prefix1.shouldReroot() != prefix2.shouldReroot()) {
- return prefix2.shouldReroot();
- }
- int rank1 = prefixes.get(prefix1);
- assert rank1 > 0;
- int rank2 = prefixes.get(prefix2);
- assert rank2 > 0;
- return rank2 > rank1;
- }
-
@Override
public String toString() {
return rootTrieNode.toString();
}
public Collection<PathPrefix> values() {
- return Collections.unmodifiableCollection(prefixes.keySet());
+ return Collections.unmodifiableCollection(prefixes);
}
private void assertValidAbstractDirectoryPathName(String name) {
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 13c569a..3629a14 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
@@ -31,11 +31,11 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.Map.Entry;
import java.util.jar.JarFile;
import java.util.zip.ZipFile;
@@ -71,17 +71,14 @@
}
/**
- * Used by rebasing {@link ResourceOracle ResourceOracles} to map from a full
- * classpath-based abstract path to an abstract path within a logical package.
- *
- * @see ResourceOracleImpl#shouldRebasePaths()
+ * Wrapper object around a resource to change its path when it is rerooted.
*/
- private static class ResourceWrapper extends AbstractResource {
+ private static class RerootedResource extends AbstractResource {
private final String path;
private final AbstractResource resource;
- public ResourceWrapper(String path, AbstractResource resource) {
- this.path = path;
+ public RerootedResource(AbstractResource resource, PathPrefix pathPrefix) {
+ this.path = pathPrefix.getRerootedPath(resource.getPath());
this.resource = resource;
}
@@ -126,6 +123,31 @@
}
}
+ private static class ResourceData implements Comparable<ResourceData> {
+ public final PathPrefix pathPrefix;
+ public final AbstractResource resource;
+
+ public ResourceData(AbstractResource resource, PathPrefix pathPrefix) {
+ this.resource = pathPrefix.shouldReroot() ? new RerootedResource(
+ resource, pathPrefix) : resource;
+ this.pathPrefix = pathPrefix;
+ }
+
+ public int compareTo(ResourceData other) {
+ // Rerooted takes precedence over not rerooted.
+ if (this.resource.wasRerooted() != other.resource.wasRerooted()) {
+ return this.resource.wasRerooted() ? 1 : -1;
+ }
+ // Compare priorities of the path prefixes, high number == high priority.
+ return this.pathPrefix.getPriority() - other.pathPrefix.getPriority();
+ }
+
+ @Override
+ public String toString() {
+ return "{" + resource + "," + pathPrefix + "}";
+ }
+ }
+
public static ClassPathEntry createEntryForUrl(TreeLogger logger, URL url)
throws URISyntaxException, IOException {
if (url.getProtocol().equals("file")) {
@@ -199,7 +221,7 @@
private Set<Resource> exposedResources = Collections.emptySet();
- private Map<String, AbstractResource> internalMap = Collections.emptyMap();
+ private Map<String, ResourceData> internalMap = Collections.emptyMap();
private PathPrefixSet pathPrefixSet = new PathPrefixSet();
@@ -258,7 +280,7 @@
* "new identity for the collections if anything changes" guarantee. Use a
* LinkedHashMap because we do not want the order to change.
*/
- final Map<String, AbstractResource> newInternalMap = new LinkedHashMap<String, AbstractResource>();
+ Map<String, ResourceData> newInternalMap = new LinkedHashMap<String, ResourceData>();
/*
* Walk across path roots (i.e. classpath entries) in priority order. This
@@ -266,87 +288,71 @@
* a resource that has already been added to the new map under construction
* to create the effect that resources founder earlier on the classpath take
* precedence.
+ *
+ * Exceptions: super has priority over non-super; and if there are two super
+ * resources with the same path, the one with the higher-priority path
+ * prefix wins.
*/
- int changeCount = 0;
- /**
- * A map from AbstractResources (across all the ClassPathEntry) to their
- * corresponding PathPrefixes. When old resources are used, an entry is
- * added to this map from the old resource to the PathPrefix that matches
- * the resource that the old resource supersedes.
- */
- Map<AbstractResource, PathPrefix> globalResourceToPrefixMap = new IdentityHashMap<AbstractResource, PathPrefix>();
for (ClassPathEntry pathRoot : classPath) {
TreeLogger branchForClassPathEntry = Messages.EXAMINING_PATH_ROOT.branch(
refreshBranch, pathRoot.getLocation(), null);
- int prevChangeCount = changeCount;
Map<AbstractResource, PathPrefix> resourceToPrefixMap = pathRoot.findApplicableResources(
branchForClassPathEntry, pathPrefixSet);
- globalResourceToPrefixMap.putAll(resourceToPrefixMap);
- for (AbstractResource newResource : resourceToPrefixMap.keySet()) {
- String resourcePath = newResource.getPath();
-
- /*
- * Check if we have a resource by this name in the newInternalMap and if
- * the matching pathPrefix overrides the pathPrefix for the previous
- * resource
- */
- AbstractResource existingNewResource = newInternalMap.get(resourcePath);
- if (existingNewResource != null
- && !pathPrefixSet.secondPrefixOverridesFirst(
- globalResourceToPrefixMap.get(existingNewResource),
- resourceToPrefixMap.get(newResource))) {
+ for (Entry<AbstractResource, PathPrefix> entry : resourceToPrefixMap.entrySet()) {
+ ResourceData newCpeData = new ResourceData(entry.getKey(),
+ entry.getValue());
+ String resourcePath = newCpeData.resource.getPath();
+ ResourceData oldCpeData = newInternalMap.get(resourcePath);
+ // Old wins unless the new resource has higher priority.
+ if (oldCpeData == null || oldCpeData.compareTo(newCpeData) < 0) {
+ newInternalMap.put(resourcePath, newCpeData);
+ } else {
Messages.IGNORING_SHADOWED_RESOURCE.log(branchForClassPathEntry,
resourcePath, null);
- continue;
}
-
- AbstractResource oldResource = internalMap.get(resourcePath);
- if (shouldUseNewResource(branchForClassPathEntry, oldResource,
- newResource)) {
- newInternalMap.put(resourcePath, newResource);
- ++changeCount;
- } else {
- assert oldResource != null;
- /*
- * Nothing changed, so carry the identity of the old one forward. Add
- * the path-prefix to the global prefix map so that future resources
- * can be compared.
- */
- newInternalMap.put(resourcePath, oldResource);
- globalResourceToPrefixMap.put(oldResource,
- resourceToPrefixMap.get(newResource));
- }
- }
- if (changeCount == prevChangeCount) {
- Messages.NO_RESOURCES_CHANGED.log(branchForClassPathEntry, null);
}
}
- if (changeCount == 0) {
- /*
- * Nothing was added or modified, but we still have to be sure we didn't
- * lose any resources.
- */
- if (newInternalMap.size() == internalMap.size()) {
- /*
- * Exit without changing the current exposed collections to maintain the
- * identity requirements described in the spec for ResourceOracle.
- */
- return;
+ /*
+ * Update the newInternalMap to preserve identity for any resources that
+ * have not changed; also record whether or not there are ANY changes.
+ *
+ * There's definitely a change if the sizes don't match; even if the sizes
+ * do match, every new resource must match an old resource for there to be
+ * no changes.
+ */
+ boolean didChange = internalMap.size() != newInternalMap.size();
+ for (String resourcePath : newInternalMap.keySet()) {
+ ResourceData newData = newInternalMap.get(resourcePath);
+ ResourceData oldData = internalMap.get(resourcePath);
+ if (shouldUseNewResource(logger, oldData, newData)) {
+ didChange = true;
+ } else {
+ if (oldData.resource != newData.resource) {
+ newInternalMap.put(resourcePath, oldData);
+ }
}
}
+ if (!didChange) {
+ // Nothing to do, keep the same identities.
+ return;
+ }
+
internalMap = newInternalMap;
- Map<String, Resource> externalMap = rerootResourcePaths(newInternalMap,
- globalResourceToPrefixMap);
- // Create a constant-time set for resources.
- Set<Resource> newResources = new HashSet<Resource>(externalMap.values());
- assert (newResources.size() == externalMap.size());
+ Map<String, Resource> externalMap = new HashMap<String, Resource>();
+ Set<Resource> externalSet = new HashSet<Resource>();
+ for (Entry<String, ResourceData> entry : internalMap.entrySet()) {
+ String path = entry.getKey();
+ ResourceData data = entry.getValue();
+ externalMap.put(path, data.resource);
+ externalSet.add(data.resource);
+ }
- // Update the gettable fields with the new (unmodifiable) data structures.
- exposedResources = Collections.unmodifiableSet(newResources);
+ // Update exposed collections with new (unmodifiable) data structures.
+ exposedResources = Collections.unmodifiableSet(externalSet);
exposedResourceMap = Collections.unmodifiableMap(externalMap);
exposedPathNames = Collections.unmodifiableSet(externalMap.keySet());
}
@@ -355,63 +361,13 @@
this.pathPrefixSet = pathPrefixSet;
}
- private Map<String, Resource> rerootResourcePaths(
- Map<String, AbstractResource> newInternalMap,
- Map<AbstractResource, PathPrefix> resourceToPrefixMap) {
-
- // Create an external map with rebased path names.
- Map<String, Resource> externalMap = new HashMap<String, Resource>();
- for (AbstractResource resource : newInternalMap.values()) {
- String path = resource.getPath();
- assert resourceToPrefixMap.get(resource) != null;
- if (externalMap.get(path) instanceof ResourceWrapper) {
- // A rerooted resource can block any other resource at this path.
- ResourceWrapper existingWrapper = (ResourceWrapper) externalMap.get(path);
- if (!pathPrefixSet.secondPrefixOverridesFirst(
- resourceToPrefixMap.get(existingWrapper.resource),
- resourceToPrefixMap.get(resource))) {
- continue;
- }
- }
-
- PathPrefix pathPrefix = resourceToPrefixMap.get(resource);
- assert pathPrefix != null;
- if (pathPrefix.shouldReroot()) {
- String rerootedPath = pathPrefix.getRerootedPath(path);
- if (externalMap.get(rerootedPath) instanceof ResourceWrapper) {
- ResourceWrapper existingWrapper = (ResourceWrapper) externalMap.get(rerootedPath);
- if (!pathPrefixSet.secondPrefixOverridesFirst(
- resourceToPrefixMap.get(existingWrapper.resource),
- resourceToPrefixMap.get(resource))) {
- // A rerooted resource blocks any other resource at this path.
- continue;
- }
- }
-
- // Try to reuse the same wrapper.
- Resource exposed = exposedResourceMap.get(rerootedPath);
- if (exposed instanceof ResourceWrapper) {
- ResourceWrapper exposedWrapper = (ResourceWrapper) exposed;
- if (exposedWrapper.resource == resource) {
- externalMap.put(rerootedPath, exposedWrapper);
- continue;
- }
- }
- // Just create a new wrapper.
- AbstractResource wrapper = new ResourceWrapper(rerootedPath, resource);
- externalMap.put(rerootedPath, wrapper);
- } else {
- externalMap.put(path, resource);
- }
- }
- return externalMap;
- }
-
- private boolean shouldUseNewResource(TreeLogger logger,
- AbstractResource oldResource, AbstractResource newResource) {
+ private boolean shouldUseNewResource(TreeLogger logger, ResourceData oldData,
+ ResourceData newData) {
+ AbstractResource newResource = newData.resource;
String resourcePath = newResource.getPath();
- if (oldResource != null) {
+ if (oldData != null) {
// Test 1: Is the resource found in a different location than before?
+ AbstractResource oldResource = oldData.resource;
if (oldResource.getClassPathEntry() == newResource.getClassPathEntry()) {
// Test 2: Has the resource changed since we last found it?
if (!oldResource.isStale()) {