/** * Rescans the associated paths to recompute the available resources. * * @param logger status and error details are written here */ public void refresh(TreeLogger logger) { PerfLogger.start("ResourceOracleImpl.refresh"); TreeLogger refreshBranch = Messages.REFRESHING_RESOURCES.branch(logger, null); /* * Allocate fresh data structures in anticipation of needing to honor the * "new identity for the collections if anything changes" guarantee. Use a * LinkedHashMap because we do not want the order to change. */ Map<String, ResourceData> newInternalMap = new LinkedHashMap<String, ResourceData>(); /* * Walk across path roots (i.e. classpath entries) in priority order. This * is a "reverse painter's algorithm", relying on being careful never to add * 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. */ for (ClassPathEntry pathRoot : classPath) { TreeLogger branchForClassPathEntry = Messages.EXAMINING_PATH_ROOT.branch(refreshBranch, pathRoot.getLocation(), null); Map<AbstractResource, PathPrefix> resourceToPrefixMap = pathRoot.findApplicableResources(branchForClassPathEntry, pathPrefixSet); 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); } } } /* * 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 (Map.Entry<String, ResourceData> entry : newInternalMap.entrySet()) { String resourcePath = entry.getKey(); ResourceData newData = entry.getValue(); 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. PerfLogger.end(); return; } internalMap = newInternalMap; 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 exposed collections with new (unmodifiable) data structures. exposedResources = Collections.unmodifiableSet(externalSet); exposedResourceMap = Collections.unmodifiableMap(externalMap); exposedPathNames = Collections.unmodifiableSet(externalMap.keySet()); PerfLogger.end(); }