Fixed the scoping of elements with intersecting packages. Change-Id: I40a143395e7eabecd4586f1674c97ac8fecc2012 Signed-off-by: Alexander Rookey <atrookey@google.com>
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue131_AddOptionsForService_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue131_AddOptionsForService_Test.java index d924229..0ee39e3 100644 --- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue131_AddOptionsForService_Test.java +++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue131_AddOptionsForService_Test.java
@@ -51,8 +51,6 @@ @Test public void should_support_Service_options() { Option option = xtext.find("code", ")", Option.class); IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET); - assertThat(descriptionsIn(scope), contain("code", "proto.code", "google.proto.code", - "com.google.proto.code", "info", "proto.info", "google.proto.info", - "com.google.proto.info")); + assertThat(descriptionsIn(scope), contain("code", "com.google.proto.code")); } }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue147_AddSupportForGroupOptions_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue147_AddSupportForGroupOptions_Test.java index 8abafc0..af74aec 100644 --- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue147_AddSupportForGroupOptions_Test.java +++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue147_AddSupportForGroupOptions_Test.java
@@ -69,8 +69,6 @@ CustomFieldOption option = xtext.find("code", ")", CustomFieldOption.class); IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), OPTION_SOURCE__TARGET); - assertThat(descriptionsIn(scope), contain("code", "proto.code", "google.proto.code", - "com.google.proto.code", "info", "proto.info", "google.proto.info", - "com.google.proto.info")); + assertThat(descriptionsIn(scope), contain("com.google.proto.code", "com.google.proto.info")); } }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue161_PackageScoping_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue161_PackageScoping_Test.java index 35ad6a2..4b132ba 100644 --- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue161_PackageScoping_Test.java +++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue161_PackageScoping_Test.java
@@ -62,7 +62,6 @@ when(reference.getEContainingClass()).thenReturn(COMPLEX_TYPE_LINK); MessageField field = xtext.find("type", " =", MessageField.class); IScope scope = scopeProvider.getScope((ComplexTypeLink) field.getType(), reference); - assertThat(descriptionsIn(scope), contain("base.shared.Type", "proto.base.shared.Type", - "google.proto.base.shared.Type", "com.google.proto.base.shared.Type")); + assertThat(descriptionsIn(scope), contain("com.google.proto.base.shared.Type")); } }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue167_PackageScopingWithNestedTypes_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue167_PackageScopingWithNestedTypes_Test.java index 6357a39..9f5b7ce 100644 --- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue167_PackageScopingWithNestedTypes_Test.java +++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue167_PackageScopingWithNestedTypes_Test.java
@@ -8,17 +8,12 @@ */ package com.google.eclipse.protobuf.bugs; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.when; - -import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn; +import static org.junit.Assert.assertEquals; import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule; import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith; -import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain; -import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.COMPLEX_TYPE; -import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.COMPLEX_TYPE_LINK; +import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.COMPLEX_TYPE_LINK__TARGET; -import org.eclipse.emf.ecore.EReference; +import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.scoping.IScope; import org.junit.Rule; import org.junit.Test; @@ -36,7 +31,6 @@ public class Issue167_PackageScopingWithNestedTypes_Test { @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule()); - @Inject private EReference reference; @Inject private ProtobufScopeProvider scopeProvider; // // Create file types.proto @@ -60,12 +54,11 @@ // repeated base.shared.Outer.Type type = 1; // } @Test public void should_include_package_intersection() { - when(reference.getEReferenceType()).thenReturn(COMPLEX_TYPE); - when(reference.getEContainingClass()).thenReturn(COMPLEX_TYPE_LINK); MessageField field = xtext.find("type", " =", MessageField.class); - IScope scope = scopeProvider.getScope((ComplexTypeLink) field.getType(), reference); - assertThat(descriptionsIn(scope), contain("base.shared.Outer.Type", "proto.base.shared.Outer.Type", - "google.proto.base.shared.Outer.Type", - "com.google.proto.base.shared.Outer.Type")); + IScope scope = scopeProvider.getScope((ComplexTypeLink) field.getType(), + COMPLEX_TYPE_LINK__TARGET); + QualifiedName unqualifiedName = QualifiedName.create("base","shared","Outer","Type"); + String qualifiedName = scope.getSingleElement(unqualifiedName).getQualifiedName().toString(); + assertEquals(qualifiedName, "com.google.proto.base.shared.Outer.Type"); } }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java index 8062b5d..ba61f9a 100644 --- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java +++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java
@@ -62,10 +62,8 @@ @Test public void should_provide_Types() { MessageField field = xtext.find("type", MessageField.class); IScope scope = scopeProvider.getScope(typeOf(field), COMPLEX_TYPE_LINK__TARGET); - assertThat(descriptionsIn(scope), containAll("Type", "proto.Type", "google.proto.Type", - "com.google.proto.Type", "Address", "proto.Address", "google.proto.Address", - "com.google.proto.Address", "Contact", "proto.Contact", "google.proto.Contact", - "com.google.proto.Contact")); + assertThat(descriptionsIn(scope), containAll("Type", "com.google.proto.Type", "Address", + "com.google.proto.Address", "Contact", "com.google.proto.Contact")); } // // Create file types.proto @@ -97,7 +95,7 @@ MessageField field = xtext.find("type", " =", MessageField.class); IScope scope = scopeProvider.getScope(typeOf(field), COMPLEX_TYPE_LINK__TARGET); assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address", - "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact")); + "Contact", "com.google.proto.Contact")); } // // Create file types.proto @@ -128,10 +126,8 @@ @Test public void should_provide_imported_Types_with_equal_package() { MessageField field = xtext.find("type", " =", MessageField.class); IScope scope = scopeProvider.getScope(typeOf(field), COMPLEX_TYPE_LINK__TARGET); - assertThat(descriptionsIn(scope), containAll("Type", "proto.Type", "google.proto.Type", - "com.google.proto.Type", "Address", "proto.Address", "google.proto.Address", - "com.google.proto.Address", "Contact", "proto.Contact", "google.proto.Contact", - "com.google.proto.Contact")); + assertThat(descriptionsIn(scope), containAll("Type", "com.google.proto.Type", "Address", + "com.google.proto.Address", "Contact", "com.google.proto.Contact")); } // // Create file types.proto @@ -163,7 +159,7 @@ MessageField field = xtext.find("type", " =", MessageField.class); IScope scope = scopeProvider.getScope(typeOf(field), COMPLEX_TYPE_LINK__TARGET); assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address", - "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact")); + "Contact", "com.google.proto.Contact")); } // // Create file public-types.proto @@ -202,7 +198,7 @@ MessageField field = xtext.find("type", " =", MessageField.class); IScope scope = scopeProvider.getScope(typeOf(field), COMPLEX_TYPE_LINK__TARGET); assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address", - "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact")); + "Contact", "com.google.proto.Contact")); } // // Create file sample_proto.proto @@ -224,13 +220,13 @@ // message Fruits { // optional Fruit grape = 1; // } - @Test public void should_provide_nearest_ComplexType() { - Enum expectedEnum = xtext.find("Fruit", " {", Enum.class); + @Test public void should_provide_ComplexType_when_packages_intersect() { MessageField field = xtext.find("grape", " =", MessageField.class); + Enum expectedEnum = (Enum) field.eContainer().eContainer().eContents().get(3); IScope scope = scopeProvider.getScope(field, COMPLEX_TYPE_LINK__TARGET); Object scopedEnum = scope - .getSingleElement(QualifiedName.create("sample", "proto", "foo", "Fruit")) + .getSingleElement(QualifiedName.create("Fruit")) .getEObjectOrProxy(); assertEquals(expectedEnum, scopedEnum); } @@ -248,8 +244,7 @@ MapType mapType = ((MapTypeLink) mapField.getType()).getTarget(); TypeLink valueType = mapType.getValueType(); IScope scope = scopeProvider.getScope(valueType, COMPLEX_TYPE_LINK__TARGET); - assertThat(descriptionsIn(scope), containAll("Foo", "proto.foo.Foo", "foo.Foo", - "Bar", "proto.foo.Bar", "foo.Bar", "sample.proto.foo.Foo", + assertThat(descriptionsIn(scope), containAll("Foo", "Bar", "sample.proto.foo.Foo", "sample.proto.foo.Bar")); }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionSource_target_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionSource_target_Test.java index 175a43a..9381ee3 100644 --- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionSource_target_Test.java +++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionSource_target_Test.java
@@ -72,9 +72,7 @@ @Test public void should_provide_sources_for_custom_option() { Option option = xtext.find("code", ")", Option.class); IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET); - assertThat(descriptionsIn(scope), contain("code", "proto.code", "google.proto.code", - "com.google.proto.code", "info", "proto.info", "google.proto.info", - "com.google.proto.info")); + assertThat(descriptionsIn(scope), contain("code", "com.google.proto.code")); } // // Create file custom-options.proto @@ -120,9 +118,7 @@ @Test public void should_provide_imported_sources_for_custom_option_with_equal_package() { Option option = xtext.find("code", ")", Option.class); IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET); - assertThat(descriptionsIn(scope), contain("code", "proto.code", "google.proto.code", - "com.google.proto.code", "info", "proto.info", "google.proto.info", - "com.google.proto.info")); + assertThat(descriptionsIn(scope), contain("code", "com.google.proto.code")); } // syntax = "proto2"; @@ -140,8 +136,6 @@ @Test public void should_provide_sources_for_custom_field_option() { CustomFieldOption option = xtext.find("code", ")", CustomFieldOption.class); IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET); - assertThat(descriptionsIn(scope), contain("code", "proto.code", "google.proto.code", - "com.google.proto.code", "info", "proto.info", "google.proto.info", - "com.google.proto.info")); + assertThat(descriptionsIn(scope), contain("code", "com.google.proto.code")); } }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportScope.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportScope.java index 25e8514..cdec890 100644 --- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportScope.java +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportScope.java
@@ -9,10 +9,7 @@ package com.google.eclipse.protobuf.scoping; import static com.google.eclipse.protobuf.model.util.QualifiedNames.removeLeadingDot; - import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.List; import org.eclipse.emf.ecore.EClass; @@ -24,81 +21,45 @@ import org.eclipse.xtext.scoping.impl.ImportNormalizer; import org.eclipse.xtext.scoping.impl.ImportScope; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; - /** - * {@link ImportScope} that allows additional ImportNormalizers to be added. + * {@link ImportScope} with support for intersecting packages. * * @author (atrookey@google.com) Alexander Rookey */ public class ProtobufImportScope extends ImportScope { - private final EClass type; - private List<ImportNormalizer> normalizers; + private final List<ImportNormalizer> importNormalizersForIntersectingPackages; + private EClass type; public ProtobufImportScope( List<ImportNormalizer> namespaceResolvers, + List<ImportNormalizer> intersectingNamespaceResolvers, IScope parent, ISelectable importFrom, EClass type, boolean ignoreCase) { super(namespaceResolvers, parent, importFrom, type, ignoreCase); + this.importNormalizersForIntersectingPackages = intersectingNamespaceResolvers; this.type = type; - this.normalizers = removeDuplicates(namespaceResolvers); } - /* - * Override {@link ImportScope.getAliasedElements(Iterable<IEObjectDescription>)} to use local - * {@link ImportNormalizer} list. - */ - @Override - protected Iterable<IEObjectDescription> getAliasedElements( - Iterable<IEObjectDescription> candidates) { - Multimap<QualifiedName, IEObjectDescription> keyToDescription = LinkedHashMultimap.create(); - Multimap<QualifiedName, ImportNormalizer> keyToNormalizer = HashMultimap.create(); - - for (IEObjectDescription imported : candidates) { - QualifiedName fullyQualifiedName = imported.getName(); - for (ImportNormalizer normalizer : normalizers) { - QualifiedName alias = normalizer.deresolve(fullyQualifiedName); - if (alias != null) { - QualifiedName key = alias; - if (isIgnoreCase()) { - key = key.toLowerCase(); - } - keyToDescription.put(key, new AliasedEObjectDescription(alias, imported)); - keyToNormalizer.put(key, normalizer); - } - } - } - for (QualifiedName name : keyToNormalizer.keySet()) { - if (keyToNormalizer.get(name).size() > 1) keyToDescription.removeAll(name); - } - return keyToDescription.values(); - } - - /* - * Override {@link ImportScope.getLocalElementsByName(QualifiedName)} to use local - * {@link ImportNormalizer} list. - */ @Override protected Iterable<IEObjectDescription> getLocalElementsByName(QualifiedName name) { + Iterable<IEObjectDescription> localElementsByName = super.getLocalElementsByName(name); + if (!localElementsByName.iterator().hasNext()) { + localElementsByName = getLocalElementsFromIntersectingPackages(name); + } + return localElementsByName; + } + + private Iterable<IEObjectDescription> getLocalElementsFromIntersectingPackages(QualifiedName name) { List<IEObjectDescription> result = new ArrayList<>(); - QualifiedName resolvedQualifiedName = null; ISelectable importFrom = getImportFrom(); - for (ImportNormalizer normalizer : normalizers) { + for (ImportNormalizer normalizer : importNormalizersForIntersectingPackages) { final QualifiedName resolvedName = normalizer.resolve(name); if (resolvedName != null) { Iterable<IEObjectDescription> resolvedElements = importFrom.getExportedObjects(type, resolvedName, isIgnoreCase()); for (IEObjectDescription resolvedElement : resolvedElements) { - if (resolvedQualifiedName == null) resolvedQualifiedName = resolvedName; - else if (!resolvedQualifiedName.equals(resolvedName)) { - if (result.get(0).getEObjectOrProxy() != resolvedElement.getEObjectOrProxy()) { - return Collections.emptyList(); - } - } QualifiedName alias = normalizer.deresolve(resolvedElement.getName()); if (alias == null) throw new IllegalStateException( @@ -117,12 +78,4 @@ public IEObjectDescription getSingleElement(QualifiedName name) { return super.getSingleElement(removeLeadingDot(name)); } - - public void addNormalizer(ImportNormalizer normalizer) { - normalizers.add(normalizer); - } - - public void addAllNormalizers(Collection<ImportNormalizer> normalizer) { - normalizers.addAll(normalizer); - } }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportedNamespaceAwareLocalScopeProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportedNamespaceAwareLocalScopeProvider.java index e37db31..43294fa 100644 --- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportedNamespaceAwareLocalScopeProvider.java +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportedNamespaceAwareLocalScopeProvider.java
@@ -8,11 +8,12 @@ */ package com.google.eclipse.protobuf.scoping; -import java.lang.reflect.Array; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + import java.util.ArrayList; import java.util.List; -import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; @@ -48,55 +49,18 @@ ISelectable importFrom, EClass type, boolean ignoreCase) { - return new ProtobufImportScope(namespaceResolvers, parent, importFrom, type, ignoreCase); + return createImportScope(parent, namespaceResolvers, emptyList(), importFrom, type, ignoreCase); } - @Override - protected List<ImportNormalizer> internalGetImportedNamespaceResolvers( - final EObject context, boolean ignoreCase) { - List<ImportNormalizer> importedNamespaceResolvers = new ArrayList<>(); - EList<EObject> eContents = context.eContents(); - for (EObject child : eContents) { - String name = getImportedNamespace(child); - if (name != null && !name.isEmpty()) { - ImportNormalizer resolver = createImportedNamespaceResolver(name, ignoreCase); - if (resolver != null) { - importedNamespaceResolvers.add(resolver); - } - importedNamespaceResolvers.addAll(createResolversForInnerNamespaces(name, ignoreCase)); - } - } - return importedNamespaceResolvers; - } - - /** - * Creates resolvers required for scoping to handle intersecting packages. The imported namespace - * {@code com.google.proto.foo} requires the following resolvers: - * - * <ul> - * <li>{@code com.*} - * <li>{@code com.google.*} - * <li>{@code com.google.proto.*} - * </ul> - * - * @param namespace the namespace. - * @param ignoreCase {@code true} if the resolver should be case insensitive. - * @return a list of the resolvers for an imported namespace - */ - private List<ImportNormalizer> createResolversForInnerNamespaces( - String namespace, boolean ignoreCase) { - String[] splitValue = namespace.split("\\."); - List<ImportNormalizer> importedNamespaceResolvers = new ArrayList<>(); - String currentNamespaceResolver = ""; - for (int i = 0; i < Array.getLength(splitValue) - 1; i++) { - currentNamespaceResolver += splitValue[i] + "."; - ImportNormalizer resolver = - createImportedNamespaceResolver(currentNamespaceResolver, ignoreCase); - if (resolver != null) { - importedNamespaceResolvers.add(resolver); - } - } - return importedNamespaceResolvers; + protected ImportScope createImportScope( + IScope parent, + List<ImportNormalizer> namespaceResolvers, + List<ImportNormalizer> intersectingNormalizers, + ISelectable importFrom, + EClass type, + boolean ignoreCase) { + return new ProtobufImportScope( + namespaceResolvers, intersectingNormalizers, parent, importFrom, type, ignoreCase); } /** Creates an {@link ImportNormalizer} with wildcards. */ @@ -112,11 +76,106 @@ return doCreateImportNormalizer(importedNamespace, WILDCARD, ignoreCase); } - /** Makes {@code getLocalElementsScope()} visible to {@link ProtobufScopeProvider} */ + /** + * Creates {@code ImportNormalizer}s to correctly scope intersecting packages. + * The package {@code com.google.proto.foo} requires the following normalizers: + * + * <ul> + * <li>{@code com.google.proto.*} + * <li>{@code com.google.*} + * <li>{@code com.*} + * </ul> + * + * The {@code ImportNormalizer}s must be ordered by decreasing length. + * This ensures that the listed returned by + * {@link ProtobufImportScope.getLocalElementsFromIntersectingPackages()} + * includes the best matching element first. + * + * @param package The package of the protocol buffer resource. + * @param ignoreCase {@code true} if the resolver should be case insensitive. + * @return {@code ImportNormalizer}s for the packages intersecting {@code package}. + */ + private List<ImportNormalizer> getImportNormalizersForIntersectingPackages( + QualifiedName protoPackage, boolean ignoreCase) { + List<ImportNormalizer> intersectingNormalizers = new ArrayList<>(); + for (int i = 1; i < protoPackage.getSegmentCount(); i++) { + intersectingNormalizers.add(doCreateImportNormalizer(protoPackage.skipLast(i), WILDCARD, ignoreCase)); + } + return intersectingNormalizers; + } + + /** + * Includes support for intersecting packages. + */ @Override + protected IScope getLocalElementsScope(IScope parent, EObject context, EReference reference) { + IScope result = parent; + ISelectable allDescriptions = getAllDescriptions(context.eResource()); + QualifiedName name = getQualifiedNameOfLocalElement(context); + boolean ignoreCase = isIgnoreCase(reference); + final List<ImportNormalizer> namespaceResolvers = + getImportedNamespaceResolvers(context, ignoreCase); + if (!namespaceResolvers.isEmpty()) { + if (isRelativeImport() && name != null && !name.isEmpty()) { + ImportNormalizer localNormalizer = doCreateImportNormalizer(name, WILDCARD, ignoreCase); + result = + createImportScope( + result, + singletonList(localNormalizer), + allDescriptions, + reference.getEReferenceType(), + isIgnoreCase(reference)); + } + List<ImportNormalizer> intersectingNormalizers; + // Only a package has the structural feature "importedNamespace". + // Therefore there should only be one. + if (namespaceResolvers.size() == 1) { + ImportNormalizer normalizer = namespaceResolvers.get(0); + intersectingNormalizers = + getImportNormalizersForIntersectingPackages( + normalizer.getImportedNamespacePrefix(), ignoreCase); + } else { + intersectingNormalizers = emptyList(); + } + result = + createImportScope( + result, + namespaceResolvers, + intersectingNormalizers, + null, + reference.getEReferenceType(), + isIgnoreCase(reference)); + } + if (name != null) { + ImportNormalizer localNormalizer = doCreateImportNormalizer(name, WILDCARD, ignoreCase); + result = + createImportScope( + result, + singletonList(localNormalizer), + allDescriptions, + reference.getEReferenceType(), + isIgnoreCase(reference)); + } + return result; + } + + /** + * Create an {@link ImportScope} using a {@code List<ImportNormalizer>} generated in + * {@link ProtobufScopeProvider} + */ protected IScope getLocalElementsScope( - IScope parent, EObject context, EReference reference) { - return super.getLocalElementsScope(parent, context, reference); + IScope parent, + final EObject context, + final EReference reference, + List<ImportNormalizer> normalizers) { + IScope result = parent; + ISelectable allDescriptions = getAllDescriptions(context.eResource()); + return createImportScope( + result, + normalizers, + allDescriptions, + reference.getEReferenceType(), + isIgnoreCase(reference)); } /** Makes {@code getResourceScope()} visible to {@link ProtobufScopeProvider} */
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java index 5baf2c7..ab63d3b 100644 --- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
@@ -10,6 +10,8 @@ import static com.google.eclipse.protobuf.util.Tracer.DEBUG_SCOPING; import static com.google.eclipse.protobuf.validation.ProtobufResourceValidator.getScopeProviderTimingCollector; +import static java.util.Collections.singletonList; + import com.google.eclipse.protobuf.naming.ProtobufQualifiedNameConverter; import com.google.eclipse.protobuf.naming.ProtobufQualifiedNameProvider; import com.google.eclipse.protobuf.protobuf.ComplexType; @@ -136,22 +138,24 @@ scope = getGlobalScopeProvider().getScope(complexType.eResource(), reference); List<ImportNormalizer> normalizers = createImportNormalizersForComplexType(complexType, false); - scope = createProtobufImportScope(scope, complexType, reference); - ((ProtobufImportScope) scope).addAllNormalizers(normalizers); + scope = createProtobufImportScope(scope, complexType, reference, normalizers); } } if (indexedElement instanceof Group) { Group group = (Group) indexedElement; scope = getGlobalScopeProvider().getScope(group.eResource(), reference); ImportNormalizer normalizer = createImportNormalizerForEObject(group, false); - scope = createProtobufImportScope(scope, group, reference); - ((ProtobufImportScope) scope).addNormalizer(normalizer); + scope = createProtobufImportScope(scope, group, reference, singletonList(normalizer)); } scopeMap.put(reference, scope); } return scopeMap.get(reference); } + /** + * Let the local scope provider compute the list of {@link ImportNormalizer}s to be associated with + * the {@link ProtobufImportScope}. + */ private IScope createProtobufImportScope(IScope parent, EObject context, EReference reference) { IScope scope = parent; if (context.eContainer() == null) { @@ -162,11 +166,27 @@ return getLocalScopeProvider().getLocalElementsScope(scope, context, reference); } + /** + * Associate the {@link ProtobufImportScope} with the list of {@link ImportNormalizer}s + * passed as an argument. + */ + private IScope createProtobufImportScope( + IScope parent, EObject context, EReference reference, List<ImportNormalizer> normalizers) { + IScope scope = parent; + if (context.eContainer() == null) { + scope = getLocalScopeProvider().getResourceScope(scope, context, reference); + } else { + scope = createProtobufImportScope(scope, context.eContainer(), reference, normalizers); + } + return getLocalScopeProvider().getLocalElementsScope(scope, context, reference, normalizers); + } + /** Returns descriptor associated with the current project. */ private @Nullable Resource getDescriptorResource(EObject context) { IProject project = EResources.getProjectOf(context.eResource()); ResourceSet resourceSet = context.eResource().getResourceSet(); - ProtoDescriptorProvider.ProtoDescriptorInfo descriptorInfo = descriptorProvider.primaryDescriptor(project); + ProtoDescriptorProvider.ProtoDescriptorInfo descriptorInfo = + descriptorProvider.primaryDescriptor(project); return resourceSet.getResource(descriptorInfo.location, true); } @@ -406,8 +426,10 @@ .createImportedNamespaceResolver(descriptorMessage, false); scope = createProtobufImportScope( - scope, getDescriptorResource(optionSource).getContents().get(0), reference); - ((ProtobufImportScope) scope).addNormalizer(normalizer); + scope, + descriptorResource.getContents().get(0), + reference, + singletonList(normalizer)); return scope; } });