Index: editor/tools/plugins/com.google.dart.engine.services/src/com/google/dart/engine/services/completion/CompletionEngine.java |
diff --git a/editor/tools/plugins/com.google.dart.engine.services/src/com/google/dart/engine/services/completion/CompletionEngine.java b/editor/tools/plugins/com.google.dart.engine.services/src/com/google/dart/engine/services/completion/CompletionEngine.java |
index d22b8626b70695d380106caa9b629b3de459343c..45b88ff7a51fa48207ffe54e7063ad2113a8cab0 100644 |
--- a/editor/tools/plugins/com.google.dart.engine.services/src/com/google/dart/engine/services/completion/CompletionEngine.java |
+++ b/editor/tools/plugins/com.google.dart.engine.services/src/com/google/dart/engine/services/completion/CompletionEngine.java |
@@ -147,6 +147,7 @@ import org.apache.commons.lang3.StringUtils; |
import java.io.File; |
import java.net.URI; |
import java.util.ArrayList; |
+import java.util.Arrays; |
import java.util.Collection; |
import java.util.Collections; |
import java.util.HashMap; |
@@ -274,6 +275,7 @@ public class CompletionEngine { |
class NameCollector { |
private Map<String, List<Element>> uniqueNames = new HashMap<String, List<Element>>(); |
+ |
private Set<Element> potentialMatches; |
public NameCollector() { |
@@ -299,10 +301,6 @@ public class CompletionEngine { |
} |
} |
- void addNamesDefinedByClassElement(ClassElement classElement) { |
- addNamesDefinedByType(classElement.getType()); |
- } |
- |
void addNamesDefinedByExecutable(ExecutableElement execElement) { |
mergeNames(execElement.getParameters()); |
mergeNames(execElement.getLocalVariables()); |
@@ -313,37 +311,21 @@ public class CompletionEngine { |
addNamesDefinedByTypeHierarchy(classElement.getType(), forSuper); |
} |
- void addNamesDefinedByInterfaceTypes(InterfaceType[] types) { |
- for (InterfaceType type : types) { |
- addNamesDefinedByType(type); |
- } |
- } |
- |
void addNamesDefinedByType(Type type) { |
- if (type instanceof InterfaceType) { |
- addNamesDefinedByInterfaceType((InterfaceType) type); |
- } else if (type instanceof UnionType) { |
- for (Type t : ((UnionType) type).getElements()) { |
- addNamesDefinedByType(t); |
- } |
- } else { |
- // Or should we just raise an exception? |
- AnalysisEngine.getInstance().getLogger().logError( |
- "Unexpected type in [NameCollector.addNamesDefinedByType]: " + type); |
- } |
+ mergeNames(namesDefinedByType(type)); |
} |
void addNamesDefinedByTypeHierarchy(Type type, boolean forSuper) { |
- if (type instanceof InterfaceType) { |
- addNamesDefinedByInterfaceTypeHierarchy((InterfaceType) type, forSuper); |
- } else if (type instanceof UnionType) { |
- for (Type t : ((UnionType) type).getElements()) { |
- addNamesDefinedByTypeHierarchy(t, forSuper); |
+ mergeNames(namesDefinedByTypeHierarchy(type, forSuper)); |
+ // Collect names defined by subtypes separately so they can be identified later. |
+ NameCollector potentialMatchCollector = createNameCollector(); |
+ potentialMatchCollector.mergeNames(potentialNamesDefinedByTypeHierarchy(type)); |
+ potentialMatches = new HashSet<Element>(potentialMatchCollector.uniqueNames.size()); |
+ for (List<Element> matches : potentialMatchCollector.uniqueNames.values()) { |
+ for (Element match : matches) { |
+ mergeName(match); |
+ potentialMatches.add(match); |
} |
- } else { |
- // Or should we just raise an exception? |
- AnalysisEngine.getInstance().getLogger().logError( |
- "Unexpected type in [NameCollector.addNamesDefinedByTypeHierarchy]: " + type); |
} |
} |
@@ -395,39 +377,6 @@ public class CompletionEngine { |
} |
} |
- private void addNamesDefinedByInterfaceType(InterfaceType type) { |
- if (inPrivateLibrary(type)) { |
- return; |
- } |
- PropertyAccessorElement[] accessors = type.getAccessors(); |
- mergeNames(accessors); |
- MethodElement[] methods = type.getMethods(); |
- mergeNames(methods); |
- mergeNames(type.getElement().getTypeParameters()); |
- filterStaticRefs(accessors); |
- filterStaticRefs(methods); |
- } |
- |
- private void addNamesDefinedByInterfaceTypeHierarchy(InterfaceType type, boolean forSuper) { |
- InterfaceType[] superTypes = type.getElement().getAllSupertypes(); |
- if (!forSuper) { |
- superTypes = ArrayUtils.add(superTypes, 0, type); |
- } |
- addNamesDefinedByInterfaceTypes(superTypes); |
- // Collect names defined by subtypes separately so they can be identified later. |
- NameCollector potentialMatchCollector = createNameCollector(); |
- if (!type.isObject()) { |
- potentialMatchCollector.addNamesDefinedByInterfaceTypes(allSubtypes(type.getElement())); |
- } |
- potentialMatches = new HashSet<Element>(potentialMatchCollector.uniqueNames.size()); |
- for (List<Element> matches : potentialMatchCollector.uniqueNames.values()) { |
- for (Element match : matches) { |
- mergeName(match); |
- potentialMatches.add(match); |
- } |
- } |
- } |
- |
private void addTopLevelNames(List<Element> elements) { |
mergeNames(findAllTypes(elements)); |
if (!state.areClassesRequired) { |
@@ -436,18 +385,17 @@ public class CompletionEngine { |
} |
} |
- private void filterStaticRefs(ExecutableElement[] elements) { |
+ private Set<Element> filterStaticRefs(ExecutableElement[] elements) { |
+ Set<Element> filteredElements = new HashSet<Element>(); |
for (ExecutableElement execElem : elements) { |
- if (state.areInstanceReferencesProhibited && !execElem.isStatic()) { |
- remove(execElem); |
- } else if (state.areStaticReferencesProhibited && execElem.isStatic()) { |
- remove(execElem); |
- } else if (!state.areOperatorsAllowed && execElem.isOperator()) { |
- remove(execElem); |
- } else if (state.areMethodsProhibited && !execElem.isOperator()) { |
- remove(execElem); |
+ if (!(state.areInstanceReferencesProhibited && !execElem.isStatic() |
+ || state.areStaticReferencesProhibited && execElem.isStatic() |
+ || !state.areOperatorsAllowed && execElem.isOperator() || state.areMethodsProhibited |
+ && !execElem.isOperator())) { |
+ filteredElements.add(execElem); |
} |
} |
+ return filteredElements; |
} |
private boolean inPrivateLibrary(InterfaceType type) { |
@@ -463,6 +411,36 @@ public class CompletionEngine { |
return true; |
} |
+ /** |
+ * Return the set of elements whose names occur in sets in {@code elementSets}. |
+ * |
+ * @param elementSets |
+ * @return |
+ */ |
+ private Set<Element> intersection(Collection<Set<Element>> elementSets) { |
+ if (elementSets.isEmpty()) { |
+ return Collections.emptySet(); |
+ } |
+ |
+ Iterator<Set<Element>> i = elementSets.iterator(); |
+ Set<String> commonNames = namesOfElements(i.next()); |
+ // Compute names common to all element collections. |
+ while (i.hasNext()) { |
+ // Intersection. |
+ commonNames.retainAll(namesOfElements(i.next())); |
+ } |
+ // Compute elements with common names. |
+ Set<Element> elements = new HashSet<Element>(); |
+ for (Collection<Element> es : elementSets) { |
+ for (Element e : es) { |
+ if (commonNames.contains(e.getName())) { |
+ elements.add(e); |
+ } |
+ } |
+ } |
+ return elements; |
+ } |
+ |
private void mergeName(Element element) { |
if (element == null) { |
return; |
@@ -483,11 +461,148 @@ public class CompletionEngine { |
dups.add(element); |
} |
- private void mergeNames(Element[] elements) { |
+ private void mergeNames(Collection<Element> elements) { |
for (Element element : elements) { |
mergeName(element); |
} |
} |
+ |
+ private void mergeNames(Element[] elements) { |
+ mergeNames(Arrays.asList(elements)); |
+ } |
+ |
+ private Set<Element> namesDefinedByInterfaceType(InterfaceType type) { |
+ if (inPrivateLibrary(type)) { |
+ return Collections.emptySet(); |
+ } |
+ Set<Element> elements = new HashSet<Element>(); |
+ PropertyAccessorElement[] accessors = type.getAccessors(); |
+ elements.addAll(filterStaticRefs(accessors)); |
+ MethodElement[] methods = type.getMethods(); |
+ elements.addAll(filterStaticRefs(methods)); |
+ elements.addAll(Arrays.asList(type.getElement().getTypeParameters())); |
+ return elements; |
+ } |
+ |
+ private Set<Element> namesDefinedByInterfaceTypeHierarchy(InterfaceType type, boolean forSuper) { |
+ InterfaceType[] superTypes = type.getElement().getAllSupertypes(); |
+ if (!forSuper) { |
+ superTypes = ArrayUtils.add(superTypes, 0, type); |
+ } |
+ return namesDefinedByInterfaceTypes(superTypes); |
+ } |
+ |
+ private Set<Element> namesDefinedByInterfaceTypes(InterfaceType[] types) { |
+ Set<Element> elements = new HashSet<Element>(); |
+ for (InterfaceType type : types) { |
+ elements.addAll(namesDefinedByType(type)); |
+ } |
+ return elements; |
+ } |
+ |
+ // The functions [namesDefinedByType], [namesDefinedByTypeHierarchy], |
+ // and [potentialNamesDefinedByTypeHierarchy] all have the same |
+ // structure, but they are not easy to combine in Java w/o higher |
+ // order functions ([union], [intersection], [*namesDefinedByInterfaceType*]). |
+ private Set<Element> namesDefinedByType(Type type) { |
+ if (type instanceof InterfaceType) { |
+ return namesDefinedByInterfaceType((InterfaceType) type); |
+ } else if (type instanceof UnionType) { |
+ List<Set<Element>> nameSets = new ArrayList<Set<Element>>(); |
+ for (Type t : ((UnionType) type).getElements()) { |
+ nameSets.add(namesDefinedByType(t)); |
+ } |
+ // For strict union types a field/method is only defined on the |
+ // union if it's defined on *all* member types. For non-strict union types |
+ // a field/method is defined if it's defined on *any* member type. |
+ if (AnalysisEngine.getInstance().getStrictUnionTypes()) { |
+ // TODO(collinsn): fix the case where multiple members define the same |
+ // name with different types. |
+ return intersection(nameSets); |
+ } else { |
+ // TODO(collinsn): this doesn't quite do the right thing, since |
+ // other code in this class uniquifies this list by name. See usage |
+ // of the [uniqueNames] field. |
+ return union(nameSets); |
+ } |
+ } else { |
+ // Or should we just raise an exception? |
+ AnalysisEngine.getInstance().getLogger().logError( |
+ "Unexpected type in [NameCollector.namesDefinedByType]: " + type); |
+ return Collections.emptySet(); |
+ } |
+ } |
+ |
+ // See [namesDefinedByType]. |
+ private Set<Element> namesDefinedByTypeHierarchy(Type type, boolean forSuper) { |
+ if (type instanceof InterfaceType) { |
+ return namesDefinedByInterfaceTypeHierarchy((InterfaceType) type, forSuper); |
+ } else if (type instanceof UnionType) { |
+ List<Set<Element>> nameSets = new ArrayList<Set<Element>>(); |
+ for (Type t : ((UnionType) type).getElements()) { |
+ nameSets.add(namesDefinedByTypeHierarchy(t, forSuper)); |
+ } |
+ if (AnalysisEngine.getInstance().getStrictUnionTypes()) { |
+ return intersection(nameSets); |
+ } else { |
+ return union(nameSets); |
+ } |
+ } else { |
+ AnalysisEngine.getInstance().getLogger().logError( |
+ "Unexpected type in [NameCollector.namesDefinedByTypeHierarchy]: " + type); |
+ return Collections.emptySet(); |
+ } |
+ } |
+ |
+ private Set<String> namesOfElements(Collection<Element> elements) { |
+ Set<String> names = new HashSet<String>(); |
+ for (Element e : elements) { |
+ names.add(e.getName()); |
+ } |
+ return names; |
+ } |
+ |
+ private Set<Element> potentialNamesDefinedByInterfaceTypeHierarchy(InterfaceType type) { |
+ if (!type.isObject()) { |
+ return namesDefinedByInterfaceTypes(allSubtypes(type.getElement())); |
+ } else { |
+ return Collections.emptySet(); |
+ } |
+ } |
+ |
+ // See [namesDefinedByType]. |
+ // |
+ // The use of [union] and [intersection] here is dual to the usage in |
+ // the related methods. The point is that marking a method as potential |
+ // is more conservative, so e.g. we mark a method as potential on a |
+ // union type if it's potential on *any* member type. |
+ private Set<Element> potentialNamesDefinedByTypeHierarchy(Type type) { |
+ if (type instanceof InterfaceType) { |
+ return potentialNamesDefinedByInterfaceTypeHierarchy((InterfaceType) type); |
+ } else if (type instanceof UnionType) { |
+ List<Set<Element>> nameSets = new ArrayList<Set<Element>>(); |
+ for (Type t : ((UnionType) type).getElements()) { |
+ nameSets.add(potentialNamesDefinedByTypeHierarchy(t)); |
+ } |
+ if (AnalysisEngine.getInstance().getStrictUnionTypes()) { |
+ return union(nameSets); |
+ } else { |
+ return intersection(nameSets); |
+ } |
+ } else { |
+ AnalysisEngine.getInstance().getLogger().logError( |
+ "Unexpected type in [NameCollector.potentialNamesDefinedByTypeHierarchy]: " + type); |
+ return Collections.emptySet(); |
+ } |
+ } |
+ |
+ private Set<Element> union(Collection<Set<Element>> elementSets) { |
+ Set<Element> elements = new HashSet<Element>(); |
+ for (Set<Element> es : elementSets) { |
+ elements.addAll(es); |
+ } |
+ return elements; |
+ } |
} |
enum TopLevelNamesKind { |