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);
      }
    }
  }
 /* @NonNull */
 @Override
 public Iterable<Pair<IStorage, IProject>> getStorages(/* @NonNull */ URI uri) {
   List<Pair<IStorage, IProject>> result = newArrayListWithCapacity(1);
   List<PackageFragmentRootData> packageFragmentRootDatas;
   synchronized (cachedPackageFragmentRootData) {
     packageFragmentRootDatas = newArrayList(cachedPackageFragmentRootData.values());
   }
   Iterator<PackageFragmentRootData> iterator = packageFragmentRootDatas.iterator();
   while (iterator.hasNext()) {
     PackageFragmentRootData data = iterator.next();
     if (data.exists()) {
       if (data.uriPrefix == null || uri.toString().startsWith(data.uriPrefix.toString())) {
         IStorage storage = data.uri2Storage.get(uri);
         if (storage != null) {
           for (IPackageFragmentRoot root : data.associatedRoots.values()) {
             result.add(Tuples.create(storage, root.getJavaProject().getProject()));
           }
         }
       }
     } else {
       iterator.remove();
     }
   }
   if (result.isEmpty() && uri.isArchive()) {
     String authority = uri.authority();
     authority = authority.substring(0, authority.length() - 1);
     URI archiveURI = URI.createURI(authority);
     if (archiveURI.isFile() || archiveURI.isPlatformResource()) {
       IPath archivePath =
           new Path(
               archiveURI.isPlatformResource()
                   ? archiveURI.toPlatformString(true)
                   : archiveURI.toFileString());
       for (PackageFragmentRootData data : packageFragmentRootDatas) {
         if (data.uriPrefix != null && archivePath.equals(data.getPath())) {
           // prefixes have an empty last segment.
           URI prefix =
               data.uriPrefix.lastSegment().length() == 0
                   ? data.uriPrefix.trimSegments(1)
                   : data.uriPrefix;
           URI expectedURI = prefix.appendSegments(uri.segments());
           IStorage storage = data.uri2Storage.get(expectedURI);
           if (storage != null) {
             for (IPackageFragmentRoot root : data.associatedRoots.values()) {
               result.add(Tuples.create(storage, root.getJavaProject().getProject()));
             }
           }
         }
       }
     }
   }
   return result;
 }
  /** @since 2.4 */
  protected PackageFragmentRootData initializeData(final IPackageFragmentRoot root) {
    final PackageFragmentRootData data =
        new PackageFragmentRootData(computeModificationStamp(root));
    data.addRoot(root);
    try {
      final SourceAttachmentPackageFragmentRootWalker<Void> walker =
          new SourceAttachmentPackageFragmentRootWalker<Void>() {

            @Override
            protected URI getURI(
                IFile file,
                org.eclipse.xtext.ui.resource.PackageFragmentRootWalker.TraversalState state) {
              if (!uriValidator.isPossiblyManaged(file)) return null;
              return super.getURI(file, state);
            }

            @Override
            protected URI getURI(
                IJarEntryResource jarEntry,
                org.eclipse.xtext.ui.resource.PackageFragmentRootWalker.TraversalState state) {
              if (!uriValidator.isPossiblyManaged(jarEntry)) return null;
              final URI uri = locator.getURI(root, jarEntry, state);
              if (!uriValidator.isValid(uri, jarEntry)) return null;
              return uri;
            }

            @Override
            protected Void handle(
                URI uri,
                IStorage storage,
                org.eclipse.xtext.ui.resource.PackageFragmentRootWalker.TraversalState state) {
              data.uri2Storage.put(uri, storage);
              return null;
            }
          };
      walker.traverse(root, false);
      if (walker.getBundleSymbolicName() != null)
        data.uriPrefix = URI.createPlatformResourceURI(walker.getBundleSymbolicName() + "/", true);
    } catch (RuntimeException e) {
      log.error(e.getMessage(), e);
    } catch (JavaModelException e) {
      log.debug(e.getMessage(), e);
    }
    return data;
  }
 private PackageFragmentRootData getCachedData(IPackageFragmentRoot root) {
   final String path = root.getPath().toString();
   synchronized (cachedPackageFragmentRootData) {
     if (cachedPackageFragmentRootData.containsKey(path)) {
       final PackageFragmentRootData data = cachedPackageFragmentRootData.get(path);
       if (isUpToDate(data, root)) {
         data.addRoot(root);
         return data;
       } else {
         cachedPackageFragmentRootData.remove(path);
       }
     }
   }
   PackageFragmentRootData data = initializeData(root);
   synchronized (cachedPackageFragmentRootData) {
     cachedPackageFragmentRootData.put(path, data);
   }
   return data;
 }