Added a cache to ModelElementFinder. - Removes the UI freeze of one second or greater when opening or editing a proto file. - Reduces time to lookup scopes of imported proto files. Change-Id: Id0b665a5f9ed776deecc54c322d6597c4c5e42b9
diff --git a/com.google.eclipse.protobuf/META-INF/MANIFEST.MF b/com.google.eclipse.protobuf/META-INF/MANIFEST.MF index f55ef48..e76431d 100644 --- a/com.google.eclipse.protobuf/META-INF/MANIFEST.MF +++ b/com.google.eclipse.protobuf/META-INF/MANIFEST.MF
@@ -9,15 +9,16 @@ Require-Bundle: org.antlr.runtime, org.apache.commons.logging, org.apache.log4j, - org.eclipse.emf.mwe2.launch;resolution:=optional, org.eclipse.core.resources, org.eclipse.core.runtime, + org.eclipse.emf.common, + org.eclipse.emf.ecore, + org.eclipse.emf.mwe2.launch;resolution:=optional, + org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.xtext, org.eclipse.xtext.generator;resolution:=optional, - org.eclipse.emf.common, org.eclipse.xtext.ui, - org.eclipse.xtext.util, - org.eclipse.emf.ecore + org.eclipse.xtext.util Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: com.google.eclipse.protobuf, com.google.eclipse.protobuf.conversion,
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ModelElementFinder.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ModelElementFinder.java index bf24be1..4520d67 100644 --- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ModelElementFinder.java +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ModelElementFinder.java
@@ -11,22 +11,8 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; - import static org.eclipse.emf.ecore.util.EcoreUtil.getAllContents; -import static com.google.common.collect.Sets.newHashSet; - -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import org.eclipse.emf.common.util.TreeIterator; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.emf.ecore.resource.ResourceSet; -import org.eclipse.xtext.resource.IEObjectDescription; - import com.google.eclipse.protobuf.model.util.Imports; import com.google.eclipse.protobuf.model.util.ModelObjects; import com.google.eclipse.protobuf.model.util.Packages; @@ -40,6 +26,23 @@ import com.google.eclipse.protobuf.resource.ResourceSets; import com.google.inject.Inject; +import org.eclipse.emf.common.util.TreeIterator; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.xtext.resource.IEObjectDescription; +import org.eclipse.xtext.util.OnChangeEvictingCache; +import org.eclipse.xtext.util.OnChangeEvictingCache.CacheAdapter; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + /** * @author alruiz@google.com (Alex Ruiz) */ @@ -51,16 +54,16 @@ @Inject private Resources resources; @Inject private ResourceSets resourceSets; - // Start at an element <T> Collection<IEObjectDescription> find(EObject start, FinderStrategy<T> strategy, T criteria) { - Set<IEObjectDescription> descriptions = newHashSet(); + Set<IEObjectDescription> descriptions = new HashSet<>(); descriptions.addAll(getDescriptionsFromObjectAncestors(start, strategy, criteria)); Protobuf root = modelObjects.rootOf(start); descriptions.addAll(getDescriptionsFromAllImports(root, strategy, criteria)); return unmodifiableSet(descriptions); } - private <T> Collection<IEObjectDescription> getDescriptionsFromObjectAncestors(EObject start, FinderStrategy<T> strategy, T criteria) { + private <T> Collection<IEObjectDescription> getDescriptionsFromObjectAncestors( + EObject start, FinderStrategy<T> strategy, T criteria) { UniqueDescriptions descriptions = new UniqueDescriptions(); EObject current = start.eContainer(); while (current != null) { @@ -71,35 +74,43 @@ } <T> Collection<IEObjectDescription> find(Protobuf start, FinderStrategy<T> strategy, T criteria) { - Set<IEObjectDescription> descriptions = newHashSet(); + Set<IEObjectDescription> descriptions = new HashSet<>(); descriptions.addAll(getDescriptionsFromObjectDescendants(start, strategy, criteria, 0)); descriptions.addAll(getDescriptionsFromAllImports(start, strategy, criteria)); return unmodifiableSet(descriptions); } - private <T> Collection<IEObjectDescription> getDescriptionsFromObjectDescendants(EObject start, FinderStrategy<T> strategy, T criteria, int level) { + private <T> Collection<IEObjectDescription> getDescriptionsFromObjectDescendants( + EObject start, FinderStrategy<T> strategy, T criteria, int level) { UniqueDescriptions descriptions = new UniqueDescriptions(); for (EObject element : start.eContents()) { descriptions.addAll(strategy.local(element, criteria, level)); if (element instanceof Message || element instanceof Group) { - descriptions.addAll(getDescriptionsFromObjectDescendants(element, strategy, criteria, level + 1)); + descriptions.addAll( + getDescriptionsFromObjectDescendants(element, strategy, criteria, level + 1)); } } return descriptions.values(); } - private <T> Collection<IEObjectDescription> getDescriptionsFromAllImports(Protobuf start, FinderStrategy<T> strategy, T criteria) { + private <T> Collection<IEObjectDescription> getDescriptionsFromAllImports( + Protobuf start, FinderStrategy<T> strategy, T criteria) { List<Import> allImports = protobufs.importsIn(start); if (allImports.isEmpty()) { return emptyList(); } ResourceSet resourceSet = start.eResource().getResourceSet(); - return getDescriptionsFromImports(allImports, modelObjects.packageOf(start), resourceSet, strategy, criteria); + return getDescriptionsFromImports( + allImports, modelObjects.packageOf(start), resourceSet, strategy, criteria); } - private <T> Collection<IEObjectDescription> getDescriptionsFromImports(List<Import> allImports, Package fromImporter, - ResourceSet resourceSet, FinderStrategy<T> strategy, T criteria) { - Set<IEObjectDescription> descriptions = newHashSet(); + private <T> Collection<IEObjectDescription> getDescriptionsFromImports( + List<Import> allImports, + Package fromImporter, + ResourceSet resourceSet, + FinderStrategy<T> strategy, + T criteria) { + Set<IEObjectDescription> descriptions = new HashSet<>(); for (Import anImport : allImports) { if (imports.isImportingDescriptor(anImport)) { descriptions.addAll(strategy.inDescriptor(anImport, criteria)); @@ -118,23 +129,35 @@ continue; } if (rootOfImported != null) { - descriptions.addAll(getDescriptionsFromPublicImports(rootOfImported, strategy, criteria)); - if (arePackagesRelated(fromImporter, rootOfImported)) { - descriptions.addAll(getDescriptionsFromObjectDescendants(rootOfImported, strategy, criteria, 0)); - continue; + CacheAdapter cache = new OnChangeEvictingCache().getOrCreate(imported); + Set<IEObjectDescription> descriptionsFromImport = + getFromCache(cache, resolvedUri, strategy, criteria); + if (descriptionsFromImport == null) { + descriptionsFromImport = new HashSet<>(); + descriptionsFromImport.addAll( + getDescriptionsFromPublicImports(rootOfImported, strategy, criteria)); + if (arePackagesRelated(fromImporter, rootOfImported)) { + descriptionsFromImport.addAll( + getDescriptionsFromObjectDescendants(rootOfImported, strategy, criteria, 0)); + } else { + Package packageOfImported = modelObjects.packageOf(rootOfImported); + TreeIterator<Object> contents = getAllContents(imported, true); + while (contents.hasNext()) { + Object next = contents.next(); + descriptionsFromImport.addAll( + strategy.imported(fromImporter, packageOfImported, next, criteria)); + } + } + putToCache(cache, resolvedUri, strategy, criteria, descriptionsFromImport); } - Package packageOfImported = modelObjects.packageOf(rootOfImported); - TreeIterator<Object> contents = getAllContents(imported, true); - while (contents.hasNext()) { - Object next = contents.next(); - descriptions.addAll(strategy.imported(fromImporter, packageOfImported, next, criteria)); - } + descriptions.addAll(descriptionsFromImport); } } return descriptions; } - private <T> Collection<IEObjectDescription> getDescriptionsFromPublicImports(Protobuf start, FinderStrategy<T> strategy, T criteria) { + private <T> Collection<IEObjectDescription> getDescriptionsFromPublicImports( + Protobuf start, FinderStrategy<T> strategy, T criteria) { if (!protobufs.hasKnownSyntax(start)) { return emptySet(); } @@ -143,7 +166,36 @@ return emptyList(); } ResourceSet resourceSet = start.eResource().getResourceSet(); - return getDescriptionsFromImports(allImports, modelObjects.packageOf(start), resourceSet, strategy, criteria); + return getDescriptionsFromImports( + allImports, modelObjects.packageOf(start), resourceSet, strategy, criteria); + } + + private <T> @Nullable Set<IEObjectDescription> getFromCache( + CacheAdapter cache, URI uri, FinderStrategy<T> strategy, T criteria) { + Map<FinderStrategy<T>, Map<T, Set<IEObjectDescription>>> strategyMap = cache.get(uri); + if (strategyMap != null) { + Map<T, Set<IEObjectDescription>> criteriaMap = strategyMap.get(strategy); + if (criteriaMap != null) { + return criteriaMap.get(criteria); + } + } + return null; + } + + private <T> void putToCache( + CacheAdapter cache, URI uri, FinderStrategy<T> strategy, T criteria, + Set<IEObjectDescription> descriptions) { + Map<FinderStrategy<T>, Map<T, Set<IEObjectDescription>>> strategyMap = cache.get(uri); + if (strategyMap == null) { + strategyMap = new HashMap<>(); + cache.set(uri, strategyMap); + } + Map<T, Set<IEObjectDescription>> criteriaMap = strategyMap.get(strategy); + if (criteriaMap == null) { + criteriaMap = new HashMap<>(); + strategyMap.put(strategy, criteriaMap); + } + criteriaMap.put(criteria, descriptions); } private boolean arePackagesRelated(Package aPackage, EObject root) { @@ -152,7 +204,8 @@ } static interface FinderStrategy<T> { - Collection<IEObjectDescription> imported(Package fromImporter, Package fromImported, Object target, T criteria); + Collection<IEObjectDescription> imported( + Package fromImporter, Package fromImported, Object target, T criteria); Collection<IEObjectDescription> inDescriptor(Import anImport, T criteria);