private void clearCache(IJavaProject project, Set<PackageFragmentRootData> toBeKept) { Collection<PackageFragmentRootData> values; synchronized (cachedPackageFragmentRootData) { values = newArrayList(cachedPackageFragmentRootData.values()); } List<PackageFragmentRootData> toBeRemoved = newArrayList(); for (PackageFragmentRootData data : values) { if (toBeKept.contains(data)) { continue; } // create a copy of the known associated roots to avoid concurrent modification // and conflicts with other readers Map<String, IPackageFragmentRoot> copy = newLinkedHashMap(data.associatedRoots); Iterator<IPackageFragmentRoot> i = copy.values().iterator(); IPackageFragmentRoot someRoot = null; boolean didChange = false; while (i.hasNext()) { IPackageFragmentRoot root = i.next(); if (project.equals(root.getJavaProject())) { i.remove(); didChange = true; } else if (someRoot == null) { someRoot = root; } } if (copy.size() == 0) { toBeRemoved.add(data); } else if (didChange) { // get rid of cached storages that still point to roots / projects that are no longer // available // and recompute them lazily on demand data.associatedRoots = copy; final IPackageFragmentRoot rootToProcess = someRoot; data.uri2Storage = new ForwardingMap<URI, IStorage>() { Map<URI, IStorage> delegate; @Override protected Map<URI, IStorage> delegate() { if (delegate == null) { PackageFragmentRootData newlyCollected = initializeData(rootToProcess); return delegate = newlyCollected.uri2Storage; } return delegate; } }; } } if (!toBeRemoved.isEmpty()) { synchronized (cachedPackageFragmentRootData) { cachedPackageFragmentRootData.values().removeAll(toBeRemoved); } } }