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;
}
});