Implements new scoping pipeline
Removes scoping methods dependent on ModelElementFinder and replaces
them with methods dependent on ProtobufImportedNamespaceAwareLocalScopeProvider.
Change-Id: I50e8691a61bbb85f277fef6748ed4724c85fdd41
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 401bb86..d924229 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
@@ -13,13 +13,12 @@
import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
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.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
-import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
import org.junit.Rule;
import org.junit.Test;
-
import com.google.eclipse.protobuf.junit.core.XtextRule;
import com.google.eclipse.protobuf.protobuf.Option;
import com.google.eclipse.protobuf.scoping.ProtobufScopeProvider;
@@ -33,7 +32,6 @@
public class Issue131_AddOptionsForService_Test {
@Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
- @Inject private EReference reference;
@Inject private ProtobufScopeProvider scopeProvider;
// syntax = "proto2";
@@ -52,10 +50,9 @@
// }
@Test public void should_support_Service_options() {
Option option = xtext.find("code", ")", Option.class);
- IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
- assertThat(descriptionsIn(scope), containAll("code", "proto.code", "google.proto.code", "com.google.proto.code",
- ".com.google.proto.code",
- "info", "proto.info", "google.proto.info", "com.google.proto.info",
- ".com.google.proto.info"));
+ 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"));
}
}
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 62a5015..8abafc0 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
@@ -12,25 +12,17 @@
import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
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.ContainAllFields.containAll;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
-import java.util.Collection;
-
-import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
import org.junit.Rule;
import org.junit.Test;
import com.google.eclipse.protobuf.junit.core.XtextRule;
import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
-import com.google.eclipse.protobuf.protobuf.Group;
-import com.google.eclipse.protobuf.protobuf.MessageField;
import com.google.eclipse.protobuf.protobuf.NativeFieldOption;
-import com.google.eclipse.protobuf.scoping.ProtoDescriptor;
-import com.google.eclipse.protobuf.scoping.ProtoDescriptorProvider;
import com.google.eclipse.protobuf.scoping.ProtobufScopeProvider;
-import com.google.eclipse.protobuf.util.EResources;
import com.google.inject.Inject;
/**
@@ -41,8 +33,6 @@
public class Issue147_AddSupportForGroupOptions_Test {
@Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
- @Inject private EReference reference;
- @Inject private ProtoDescriptorProvider descriptorProvider;
@Inject private ProtobufScopeProvider scopeProvider;
// syntax = "proto2";
@@ -54,12 +44,10 @@
// }
@Test public void should_provide_fields_for_native_option() {
NativeFieldOption option = xtext.find("deprecated", NativeFieldOption.class);
- IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
- Group group = xtext.find("membership", Group.class);
- ProtoDescriptor descriptor =
- descriptorProvider.primaryDescriptor(EResources.getProjectOf(option.eResource()));
- Collection<MessageField> optionSources = descriptor.availableOptionsFor(group);
- assertThat(descriptionsIn(scope), containAll(optionSources));
+ IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(),
+ OPTION_SOURCE__TARGET);
+ assertThat(descriptionsIn(scope), contain("ctype", "packed", "deprecated",
+ "experimental_map_key"));
}
// syntax = "proto2";
@@ -79,10 +67,10 @@
// }
@Test public void should_provide_fields_for_custom_option() {
CustomFieldOption option = xtext.find("code", ")", CustomFieldOption.class);
- IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
- assertThat(descriptionsIn(scope), containAll("code", "proto.code", "google.proto.code", "com.google.proto.code",
- ".com.google.proto.code",
- "info", "proto.info", "google.proto.info", "com.google.proto.info",
- ".com.google.proto.info"));
+ 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"));
}
}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue156_AddSupportForEnumValueOptions_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue156_AddSupportForEnumValueOptions_Test.java
index 9afbe58..e10ba9f 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue156_AddSupportForEnumValueOptions_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/Issue156_AddSupportForEnumValueOptions_Test.java
@@ -13,13 +13,12 @@
import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
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.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
-import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
import org.junit.Rule;
import org.junit.Test;
-
import com.google.eclipse.protobuf.junit.core.XtextRule;
import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
import com.google.eclipse.protobuf.scoping.ProtobufScopeProvider;
@@ -33,7 +32,6 @@
public class Issue156_AddSupportForEnumValueOptions_Test {
@Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
- @Inject private EReference reference;
@Inject private ProtobufScopeProvider scopeProvider;
// syntax = "proto2";
@@ -49,7 +47,7 @@
// }
@Test public void should_provide_fields_for_custom_field_option() {
CustomFieldOption option = xtext.find("active", ")", CustomFieldOption.class);
- IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
- assertThat(descriptionsIn(scope), containAll("active", ".active"));
+ IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+ assertThat(descriptionsIn(scope), contain("active"));
}
}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
index f66915f..4668d91 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2014 Google Inc.
+ * Copyright (c) 2014 Google Inc.
*
* All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
* Public License v1.0 which accompanies this distribution, and is available at
@@ -12,12 +12,17 @@
import static org.eclipse.xtext.util.Strings.isEmpty;
import com.google.eclipse.protobuf.model.util.Imports;
+import com.google.eclipse.protobuf.preferences.general.PreferenceNames;
import com.google.eclipse.protobuf.protobuf.Import;
import com.google.eclipse.protobuf.scoping.IImportResolver;
+import com.google.eclipse.protobuf.scoping.IUriResolver;
import com.google.inject.Inject;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EReference;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
+import org.mockito.Mockito;
import java.io.File;
@@ -31,16 +36,32 @@
return new IntegrationTestModule();
}
- private IntegrationTestModule( ) {}
+ private IntegrationTestModule() {}
- @Override protected void configure() {
+ @Override
+ protected void configure() {
binder().bind(IImportResolver.class).to(ImportResolver.class);
mockAndBind(EReference.class);
+ IPreferenceStoreAccess mockStoreAccess = Mockito.mock(IPreferenceStoreAccess.class);
+ IUriResolver mockUriResolver = Mockito.mock(IUriResolver.class);
+ binder().bind(IPreferenceStoreAccess.class).toInstance(mockStoreAccess);
+ // TODO (atrookey) Get rid of the excessive mocking.
+ binder().bind(IUriResolver.class).toInstance(mockUriResolver);
+ IPreferenceStore mockPreferenceStore = Mockito.mock(IPreferenceStore.class);
+ Mockito.when(mockStoreAccess.getWritablePreferenceStore(Mockito.anyObject()))
+ .thenReturn(mockPreferenceStore);
+ Mockito.when(mockPreferenceStore.getString(PreferenceNames.DESCRIPTOR_PROTO_PATH))
+ .thenReturn(PreferenceNames.DEFAULT_DESCRIPTOR_PATH);
+ Mockito.when(
+ mockUriResolver.resolveUri(
+ Mockito.eq(PreferenceNames.DEFAULT_DESCRIPTOR_PATH),
+ Mockito.anyObject(),
+ Mockito.anyObject()))
+ .thenReturn("platform:/plugin/com.google.eclipse.protobuf/descriptor.proto");
}
private static class ImportResolver implements IImportResolver {
- @Inject
- private Imports imports;
+ @Inject private Imports imports;
@Override
public String resolve(Import anImport) {
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 47e5fb1..80bde8e 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
@@ -8,6 +8,7 @@
*/
package com.google.eclipse.protobuf.scoping;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
@@ -17,14 +18,17 @@
import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
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.Before;
import org.junit.Rule;
import org.junit.Test;
import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.ComplexType;
import com.google.eclipse.protobuf.protobuf.ComplexTypeLink;
import com.google.eclipse.protobuf.protobuf.MessageField;
import com.google.inject.Inject;
@@ -67,12 +71,10 @@
@Test public void should_provide_Types() {
MessageField field = xtext.find("type", MessageField.class);
IScope scope = scopeProvider.getScope(typeOf(field), reference);
- assertThat(descriptionsIn(scope), containAll("Type", "proto.Type", "google.proto.Type", "com.google.proto.Type",
- ".com.google.proto.Type",
- "Address", "proto.Address", "google.proto.Address",
- "com.google.proto.Address", ".com.google.proto.Address",
- "Contact", "proto.Contact", "google.proto.Contact",
- "com.google.proto.Contact", ".com.google.proto.Contact"));
+ 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"));
}
// // Create file types.proto
@@ -103,10 +105,8 @@
@Test public void should_provide_imported_Types() {
MessageField field = xtext.find("type", " =", MessageField.class);
IScope scope = scopeProvider.getScope(typeOf(field), reference);
- assertThat(descriptionsIn(scope), containAll("test.proto.Type", ".test.proto.Type",
- "test.proto.Address", ".test.proto.Address",
- "Contact", "proto.Contact", "google.proto.Contact",
- "com.google.proto.Contact", ".com.google.proto.Contact"));
+ assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address",
+ "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact"));
}
// // Create file types.proto
@@ -137,12 +137,10 @@
@Test public void should_provide_imported_Types_with_equal_package() {
MessageField field = xtext.find("type", " =", MessageField.class);
IScope scope = scopeProvider.getScope(typeOf(field), reference);
- assertThat(descriptionsIn(scope), containAll("Type", "proto.Type", "google.proto.Type", "com.google.proto.Type",
- ".com.google.proto.Type",
- "Address", "proto.Address", "google.proto.Address",
- "com.google.proto.Address", ".com.google.proto.Address",
- "Contact", "proto.Contact", "google.proto.Contact",
- "com.google.proto.Contact", ".com.google.proto.Contact"));
+ 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"));
}
// // Create file types.proto
@@ -173,10 +171,8 @@
@Test public void should_provide_public_imported_Types() {
MessageField field = xtext.find("type", " =", MessageField.class);
IScope scope = scopeProvider.getScope(typeOf(field), reference);
- assertThat(descriptionsIn(scope), containAll("test.proto.Type", ".test.proto.Type",
- "test.proto.Address", ".test.proto.Address",
- "Contact", "proto.Contact", "google.proto.Contact",
- "com.google.proto.Contact", ".com.google.proto.Contact"));
+ assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address",
+ "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact"));
}
// // Create file public-types.proto
@@ -214,10 +210,38 @@
@Test public void should_provide_public_imported_Types_with_more_than_one_level() {
MessageField field = xtext.find("type", " =", MessageField.class);
IScope scope = scopeProvider.getScope(typeOf(field), reference);
- assertThat(descriptionsIn(scope), containAll("test.proto.Type", ".test.proto.Type",
- "test.proto.Address", ".test.proto.Address",
- "Contact", "proto.Contact", "google.proto.Contact",
- "com.google.proto.Contact", ".com.google.proto.Contact"));
+ assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address",
+ "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact"));
+ }
+
+ // // Create file sample_proto.proto
+ // syntax = "proto2";
+ // package sample.proto;
+ //
+ // enum Fruit {
+ // GRAPE = 1;
+ // }
+
+ // syntax = "proto2";
+ // package sample.proto.foo;
+ // import "sample_proto.proto";
+ //
+ // enum Fruit {
+ // GRAPE = 1;
+ // }
+ //
+ // message Fruits {
+ // optional Fruit grape = 1;
+ // }
+ @Test public void should_provide_nearest_ComplexType() {
+ MessageField field = xtext.find("grape", " =", MessageField.class);
+ ComplexTypeLink link = (ComplexTypeLink) field.getType();
+ ComplexType scopedEnum = link.getTarget(); // Enum inherits from ComplexType
+ IScope scope = scopeProvider.getScope(field, COMPLEX_TYPE_LINK__TARGET);
+ Object expectedEnum =
+ scope.getSingleElement(
+ QualifiedName.create("sample", "proto", "foo", "Fruit")).getEObjectOrProxy();
+ assertEquals(expectedEnum, scopedEnum);
}
private static ComplexTypeLink typeOf(MessageField field) {
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_ExtensionFieldName_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_ExtensionFieldName_Test.java
index 58c3c53..f3fcaa3 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_ExtensionFieldName_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_ExtensionFieldName_Test.java
@@ -13,7 +13,8 @@
import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
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.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.FIELD_NAME__TARGET;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
@@ -21,6 +22,8 @@
import org.junit.Test;
import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.ComplexValue;
+import com.google.eclipse.protobuf.protobuf.ComplexValueField;
import com.google.eclipse.protobuf.protobuf.ExtensionFieldName;
import com.google.eclipse.protobuf.protobuf.FieldName;
import com.google.eclipse.protobuf.protobuf.ValueField;
@@ -34,7 +37,6 @@
public class ProtobufScopeProvider_scope_FieldName_target_with_ExtensionFieldName_Test {
@Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
- @Inject private EReference reference;
@Inject private ProtobufScopeProvider scopeProvider;
// syntax = "proto2";
@@ -60,9 +62,11 @@
// }
// };
@Test public void should_provide_sources_for_aggregate_field() {
- ValueField field = xtext.find("google.proto.test.fileopt", "]", ValueField.class);
+ ComplexValueField valueField = xtext.find("file", " {", ComplexValueField.class);
+ ComplexValue value = valueField.getValues().get(0);
+ ValueField field = value.getFields().get(0);
ExtensionFieldName name = (ExtensionFieldName) field.getName();
- IScope scope = scopeProvider.scope_FieldName_target(name, reference);
- assertThat(descriptionsIn(scope), containAll("google.proto.test.fileopt", ".google.proto.test.fileopt"));
+ IScope scope = scopeProvider.getScope(name, FIELD_NAME__TARGET);
+ assertThat(descriptionsIn(scope), contain("google.proto.test.fileopt"));
}
}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_NormalFieldName_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_NormalFieldName_Test.java
index f52b4c1..538d71a 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_NormalFieldName_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_FieldName_target_with_NormalFieldName_Test.java
@@ -9,11 +9,12 @@
package com.google.eclipse.protobuf.scoping;
import static org.junit.Assert.assertThat;
-
import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
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.junit.matchers.ContainAllFieldsInMessage.containAllFieldsIn;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.FIELD_NAME__TARGET;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
@@ -28,7 +29,7 @@
import com.google.inject.Inject;
/**
- * Tests for <code>{@link ProtobufScopeProvider#scope_FieldName_target(FieldName, EReference)}</code>
+ * Tests for <code>{@link ProtobufScopeProvider#getScope(FieldName, EReference)}</code>
*
* @author alruiz@google.com (Alex Ruiz)
*/
@@ -56,9 +57,8 @@
@Test public void should_provide_sources_for_aggregate_field() {
ValueField field = xtext.find("code", ":", ValueField.class);
NormalFieldName name = (NormalFieldName) field.getName();
- IScope scope = scopeProvider.scope_FieldName_target(name, reference);
- Message message = xtext.find("Type", " {", Message.class);
- assertThat(descriptionsIn(scope), containAllFieldsIn(message));
+ IScope scope = scopeProvider.getScope(name, FIELD_NAME__TARGET);
+ assertThat(descriptionsIn(scope), contain("code"));
}
// syntax = "proto2";
@@ -84,9 +84,9 @@
@Test public void should_provide_sources_for_nested_field_notation_in_option() {
ValueField field = xtext.find("value", ":", ValueField.class);
NormalFieldName name = (NormalFieldName) field.getName();
- IScope scope = scopeProvider.scope_FieldName_target(name, reference);
+ IScope scope = scopeProvider.getScope(name, FIELD_NAME__TARGET);
Message message = xtext.find("Names", " {", Message.class);
- assertThat(descriptionsIn(scope), containAllFieldsIn(message));
+ assertThat(descriptionsIn(scope), contain("value"));
}
// syntax = "proto2";
@@ -107,9 +107,8 @@
@Test public void should_provide_sources_for_field_notation_in_field_option() {
ValueField field = xtext.find("code", ":", ValueField.class);
NormalFieldName name = (NormalFieldName) field.getName();
- IScope scope = scopeProvider.scope_FieldName_target(name, reference);
- Message message = xtext.find("Type", " {", Message.class);
- assertThat(descriptionsIn(scope), containAllFieldsIn(message));
+ IScope scope = scopeProvider.getScope(name, FIELD_NAME__TARGET);
+ assertThat(descriptionsIn(scope), contain("code"));
}
// syntax = "proto2";
@@ -135,8 +134,8 @@
@Test public void should_provide_sources_for_nested_field_notation_in_field_option() {
ValueField field = xtext.find("value", ":", ValueField.class);
NormalFieldName name = (NormalFieldName) field.getName();
- IScope scope = scopeProvider.scope_FieldName_target(name, reference);
+ IScope scope = scopeProvider.getScope(name, FIELD_NAME__TARGET);
Message message = xtext.find("Names", " {", Message.class);
- assertThat(descriptionsIn(scope), containAllFieldsIn(message));
+ assertThat(descriptionsIn(scope), contain("value"));
}
}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_LiteralLink_target_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_LiteralLink_target_Test.java
index 1c13b6f..aad3151 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_LiteralLink_target_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_LiteralLink_target_Test.java
@@ -9,10 +9,12 @@
package com.google.eclipse.protobuf.scoping;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertEquals;
import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
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.ContainAllLiteralsInEnum.containAllLiteralsIn;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.LITERAL_LINK__TARGET;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
@@ -20,23 +22,23 @@
import org.junit.Test;
import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.DefaultValueFieldOption;
import com.google.eclipse.protobuf.protobuf.Enum;
import com.google.eclipse.protobuf.protobuf.FieldOption;
+import com.google.eclipse.protobuf.protobuf.Literal;
import com.google.eclipse.protobuf.protobuf.LiteralLink;
+import com.google.eclipse.protobuf.protobuf.MessageField;
import com.google.eclipse.protobuf.protobuf.Option;
-import com.google.eclipse.protobuf.util.EResources;
import com.google.inject.Inject;
/**
- * Tests for <code>{@link ProtobufScopeProvider#scope_LiteralLink_target(LiteralLink, EReference)}</code>.
+ * Tests for <code>{@link ProtobufScopeProvider#getScope(LiteralLink, EReference)}</code>.
*
* @author alruiz@google.com (Alex Ruiz)
*/
public class ProtobufScopeProvider_scope_LiteralLink_target_Test {
@Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
- @Inject private EReference reference;
- @Inject private ProtoDescriptorProvider descriptorProvider;
@Inject private ProtobufScopeProvider scopeProvider;
// syntax = "proto2";
@@ -50,10 +52,10 @@
// optional Type type = 1 [default = ONE];
// }
@Test public void should_provide_Literals_for_default_value() {
- FieldOption option = xtext.find("default", FieldOption.class);
- IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
- Enum typeEnum = xtext.find("Type", " {", Enum.class);
- assertThat(descriptionsIn(scope), containAllLiteralsIn(typeEnum));
+ MessageField field = xtext.find("type", " =", MessageField.class);
+ FieldOption option = field.getFieldOptions().get(0);
+ IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+ assertThat(descriptionsIn(scope), contain("ONE", "TWO"));
}
// syntax = "proto2";
@@ -61,10 +63,8 @@
// option optimize_for = SPEED;
@Test public void should_provide_Literals_for_native_option() {
Option option = xtext.find("optimize_for", Option.class);
- IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
- Enum optimizeModeEnum = descriptorProvider.primaryDescriptor(
- EResources.getProjectOf(xtext.root().eResource())).enumByName("OptimizeMode");
- assertThat(descriptionsIn(scope), containAllLiteralsIn(optimizeModeEnum));
+ IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+ assertThat(descriptionsIn(scope), contain("SPEED", "CODE_SIZE", "LITE_RUNTIME"));
}
// syntax = "proto2";
@@ -83,9 +83,8 @@
// option (type) = ONE;
@Test public void should_provide_Literals_for_source_of_custom_option() {
Option option = xtext.find("type", ")", Option.class);
- IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
- Enum typeEnum = xtext.find("Type", " {", Enum.class);
- assertThat(descriptionsIn(scope), containAllLiteralsIn(typeEnum));
+ IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+ assertThat(descriptionsIn(scope), contain("ONE", "TWO"));
}
// syntax = "proto2";
@@ -108,9 +107,9 @@
// option (info).type = ONE;
@Test public void should_provide_Literals_for_source_of_field_of_custom_option() {
Option option = xtext.find("info", ")", Option.class);
- IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
- Enum typeEnum = xtext.find("Type", " {", Enum.class);
- assertThat(descriptionsIn(scope), containAllLiteralsIn(typeEnum));
+ IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+ xtext.find("Type", " {", Enum.class);
+ assertThat(descriptionsIn(scope), contain("ONE", "TWO"));
}
private static LiteralLink valueOf(Option option) {
@@ -124,11 +123,8 @@
// }
@Test public void should_provide_Literals_for_source_of_native_field_option() {
FieldOption option = xtext.find("ctype", FieldOption.class);
- IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
- ProtoDescriptor descriptor =
- descriptorProvider.primaryDescriptor(EResources.getProjectOf(xtext.resource()));
- Enum cTypeEnum = descriptor.enumByName("CType");
- assertThat(descriptionsIn(scope), containAllLiteralsIn(cTypeEnum));
+ IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+ assertThat(descriptionsIn(scope), contain("STRING", "CORD", "STRING_PIECE"));
}
// syntax = "proto2";
@@ -149,9 +145,9 @@
// }
@Test public void should_provide_Literals_for_source_of_custom_field_option() {
FieldOption option = xtext.find("type", ")", FieldOption.class);
- IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
- Enum typeEnum = xtext.find("Type", " {", Enum.class);
- assertThat(descriptionsIn(scope), containAllLiteralsIn(typeEnum));
+ IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+ xtext.find("Type", " {", Enum.class);
+ assertThat(descriptionsIn(scope), contain("ONE", "TWO"));
}
// syntax = "proto2";
@@ -176,9 +172,43 @@
// }
@Test public void should_provide_Literals_for_source_of_field_in_custom_field_option() {
FieldOption option = xtext.find("info", ")", FieldOption.class);
- IScope scope = scopeProvider.scope_LiteralLink_target(valueOf(option), reference);
- Enum typeEnum = xtext.find("Type", " {", Enum.class);
- assertThat(descriptionsIn(scope), containAllLiteralsIn(typeEnum));
+ IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+ xtext.find("Type", " {", Enum.class);
+ assertThat(descriptionsIn(scope), contain("ONE", "TWO"));
+ }
+
+ // // Create file sample_proto.proto
+ // syntax = "proto2";
+ // package sample.proto;
+ //
+ // message Example {
+ // enum Alpha {
+ // BAR = 1;
+ // }
+ // }
+
+ // syntax = "proto2";
+ // package example.proto;
+ // import "sample_proto.proto";
+ //
+ // message Fruits {
+ // enum Beta {
+ // BAR = 1;
+ // }
+ // optional Grape grape = 4 [default = BAR];
+ // }
+ @Test public void should_provide_Literal_nearest_to_option() {
+ // Beta
+ MessageField field = xtext.find("grape", " =", MessageField.class);
+ DefaultValueFieldOption option = (DefaultValueFieldOption) field.getFieldOptions().get(0);
+ LiteralLink link = (LiteralLink) option.getValue();
+ Enum enumBeta = (Enum)link.getTarget().eContainer();
+
+ // Alpha
+ Enum beta = xtext.find("Beta", Enum.class);
+ Literal literal = (Literal) beta.getElements().get(0);
+ Enum expectedEnum = (Enum) literal.eContainer();
+ assertEquals(expectedEnum, enumBeta);
}
private static LiteralLink valueOf(FieldOption option) {
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_ExtensionOptionField_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_ExtensionOptionField_Test.java
index d883338..4dc1113 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_ExtensionOptionField_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_ExtensionOptionField_Test.java
@@ -9,11 +9,11 @@
package com.google.eclipse.protobuf.scoping;
import static org.junit.Assert.assertThat;
-
import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
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.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
@@ -28,14 +28,13 @@
import com.google.inject.Inject;
/**
- * Tests for <code>{@link ProtobufScopeProvider#scope_OptionField_target(OptionField, EReference)}</code>.
+ * Tests for <code>{@link ProtobufScopeProvider#getScope(OptionField, EReference)}</code>.
*
* @author alruiz@google.com (Alex Ruiz)
*/
public class ProtobufScopeProvider_scope_OptionField_target_with_ExtensionOptionField_Test {
@Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
- @Inject private EReference reference;
@Inject private ProtobufScopeProvider scopeProvider;
// syntax = "proto2";
@@ -62,8 +61,8 @@
@Test public void should_provide_extend_message_fields_for_first_field_in_custom_option() {
CustomOption option = xtext.find("type", ")", CustomOption.class);
ExtensionOptionField codeOptionField = (ExtensionOptionField) option.getFields().get(0);
- IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
- assertThat(descriptionsIn(scope), containAll("active", "com.google.proto.active", ".com.google.proto.active"));
+ IScope scope = scopeProvider.getScope(codeOptionField, OPTION_SOURCE__TARGET);
+ assertThat(descriptionsIn(scope), contain("active", "com.google.proto.active"));
}
// syntax = "proto2";
@@ -92,7 +91,7 @@
@Test public void should_provide_message_fields_for_first_field_in_field_custom_option() {
CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
ExtensionOptionField codeOptionField = (ExtensionOptionField) option.getFields().get(0);
- IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
- assertThat(descriptionsIn(scope), containAll("active", "com.google.proto.active", ".com.google.proto.active"));
+ IScope scope = scopeProvider.getScope(codeOptionField, OPTION_SOURCE__TARGET);
+ assertThat(descriptionsIn(scope), contain("active", "com.google.proto.active"));
}
}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java
index f46338f..ee5ad96 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java
@@ -9,14 +9,14 @@
package com.google.eclipse.protobuf.scoping;
import static org.junit.Assert.assertThat;
-
import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
import static com.google.eclipse.protobuf.junit.core.SearchOption.IGNORE_CASE;
import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllFieldsInMessage.containAllFieldsIn;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_FIELD__TARGET;
+import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
import org.junit.Rule;
@@ -25,21 +25,20 @@
import com.google.eclipse.protobuf.junit.core.XtextRule;
import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
import com.google.eclipse.protobuf.protobuf.CustomOption;
-import com.google.eclipse.protobuf.protobuf.Group;
-import com.google.eclipse.protobuf.protobuf.Message;
+import com.google.eclipse.protobuf.protobuf.FieldOption;
+import com.google.eclipse.protobuf.protobuf.MessageField;
import com.google.eclipse.protobuf.protobuf.MessageOptionField;
import com.google.eclipse.protobuf.protobuf.OptionField;
import com.google.inject.Inject;
/**
- * Tests for <code>{@link ProtobufScopeProvider#scope_OptionField_target(OptionField, EReference)}</code>.
+ * Tests for <code>{@link ProtobufScopeProvider#getScope(OptionField, EReference)}</code>.
*
* @author alruiz@google.com (Alex Ruiz)
*/
public class ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test {
@Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
- @Inject private EReference reference;
@Inject private ProtobufScopeProvider scopeProvider;
// syntax = "proto2";
@@ -59,9 +58,8 @@
@Test public void should_provide_message_fields_for_first_field_in_custom_option() {
CustomOption option = xtext.find("type", ")", CustomOption.class);
MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
- IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
- Message typeMessage = xtext.find("Type", " {", Message.class);
- assertThat(descriptionsIn(scope), containAllFieldsIn(typeMessage));
+ IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+ assertThat(descriptionsIn(scope), contain("code", "name"));
}
// syntax = "proto2";
@@ -84,10 +82,9 @@
// option (type).code.number = 68;
@Test public void should_provide_message_fields_for_field_in_custom_option() {
CustomOption option = xtext.find("type", ")", CustomOption.class);
- MessageOptionField numberOptionField = (MessageOptionField) option.getFields().get(1);
- IScope scope = scopeProvider.scope_OptionField_target(numberOptionField, reference);
- Message codeMessage = xtext.find("Code", " {", Message.class);
- assertThat(descriptionsIn(scope), containAllFieldsIn(codeMessage));
+ OptionField numberOptionField = (MessageOptionField) option.getFields().get(1);
+ IScope scope = scopeProvider.getScope(numberOptionField, OPTION_FIELD__TARGET);
+ assertThat(descriptionsIn(scope), contain("number"));
}
// syntax = "proto2";
@@ -105,9 +102,8 @@
@Test public void should_provide_group_fields_for_first_field_in_custom_option() {
CustomOption option = xtext.find("type", ")", CustomOption.class, IGNORE_CASE);
MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
- IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
- Group groupMessage = xtext.find("Type", " =", Group.class);
- assertThat(descriptionsIn(scope), containAllFieldsIn(groupMessage));
+ IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+ assertThat(descriptionsIn(scope), contain("code", "name"));
}
// syntax = "proto2";
@@ -129,9 +125,8 @@
@Test public void should_provide_message_fields_for_first_field_in_field_custom_option() {
CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
- IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
- Message typeMessage = xtext.find("Type", " {", Message.class);
- assertThat(descriptionsIn(scope), containAllFieldsIn(typeMessage));
+ IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+ assertThat(descriptionsIn(scope), contain("code", "name"));
}
// syntax = "proto2";
@@ -157,9 +152,8 @@
@Test public void should_provide_message_fields_for_field_in_field_custom_option() {
CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
MessageOptionField numberOptionField = (MessageOptionField) option.getFields().get(1);
- IScope scope = scopeProvider.scope_OptionField_target(numberOptionField, reference);
- Message codeMessage = xtext.find("Code", " {", Message.class);
- assertThat(descriptionsIn(scope), containAllFieldsIn(codeMessage));
+ IScope scope = scopeProvider.getScope(numberOptionField, OPTION_FIELD__TARGET);
+ assertThat(descriptionsIn(scope), contain("number"));
}
// syntax = "proto2";
@@ -177,11 +171,12 @@
// optional boolean active = 1 [(type).code = 68];
// }
@Test public void should_provide_group_fields_for_first_field_in_field_custom_option() {
- CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class, IGNORE_CASE);
- MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
- IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
- Group groupMessage = xtext.find("Type", " =", Group.class);
- assertThat(descriptionsIn(scope), containAllFieldsIn(groupMessage));
+ MessageField messageField = xtext.find("active", MessageField.class);
+ EList<FieldOption> fieldOptions = messageField.getFieldOptions();
+ CustomFieldOption customFieldOption = (CustomFieldOption) fieldOptions.get(0);
+ OptionField codeOptionField = customFieldOption.getFields().get(0);
+ IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+ assertThat(descriptionsIn(scope), contain("code", "name"));
}
// syntax = "proto2";
@@ -208,7 +203,39 @@
@Test public void should_provide_field_option_from_oneof() {
CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
- IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
- assertThat(descriptionsIn(scope), containAll("foo", "bar", "baz"));
+ IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+ assertThat(descriptionsIn(scope), contain("foo", "bar", "baz"));
+ }
+
+ // syntax = "proto2";
+ //
+ // package com.google.proto;
+ //
+ // import 'google/protobuf/descriptor.proto';
+ //
+ // message Type {
+ // bool foo = 1;
+ // oneof choose_one {
+ // bool bar = 2;
+ // bool baz = 3;
+ // }
+ // oneof choose_another {
+ // bool qux = 4;
+ // bool quux = 5;
+ // }
+ // }
+ //
+ // extend google.protobuf.FieldOptions {
+ // optional Type type = 1000;
+ // }
+ //
+ // message Person {
+ // optional bool active = 1 [(type).bar = true];
+ // }
+ @Test public void should_provide_field_option_from_multiple_oneof() {
+ CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
+ MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
+ IScope scope = scopeProvider.getScope(codeOptionField, OPTION_FIELD__TARGET);
+ assertThat(descriptionsIn(scope), contain("foo", "bar", "baz", "qux", "quux"));
}
}
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 8b30f48..175a43a 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
@@ -12,37 +12,27 @@
import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
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.ContainAllFields.containAll;
-import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
-import static com.google.eclipse.protobuf.scoping.OptionType.FIELD;
-import static com.google.eclipse.protobuf.scoping.OptionType.FILE;
-
-import java.util.Collection;
-
+import static com.google.eclipse.protobuf.junit.matchers.ContainNames.contain;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
import org.junit.Rule;
import org.junit.Test;
-
import com.google.eclipse.protobuf.junit.core.XtextRule;
import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
-import com.google.eclipse.protobuf.protobuf.MessageField;
import com.google.eclipse.protobuf.protobuf.NativeFieldOption;
import com.google.eclipse.protobuf.protobuf.Option;
import com.google.eclipse.protobuf.protobuf.OptionSource;
-import com.google.eclipse.protobuf.util.EResources;
import com.google.inject.Inject;
/**
- * Tests for <code>{@link ProtobufScopeProvider#scope_OptionSource_target(OptionSource, EReference)}</code>
+ * Tests for <code>{@link ProtobufScopeProvider#getScope(OptionSource, EReference)}</code>
*
* @author alruiz@google.com (Alex Ruiz)
*/
public class ProtobufScopeProvider_scope_OptionSource_target_Test {
@Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
- @Inject private EReference reference;
- @Inject private ProtoDescriptorProvider descriptorProvider;
@Inject private ProtobufScopeProvider scopeProvider;
// syntax = "proto2";
@@ -50,11 +40,10 @@
// option optimize_for = SPEED;
@Test public void should_provide_sources_for_native_option() {
Option option = xtext.find("optimize_for", Option.class);
- IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
- ProtoDescriptor descriptor =
- descriptorProvider.primaryDescriptor(EResources.getProjectOf(xtext.resource()));
- Collection<MessageField> optionSources = descriptor.optionsOfType(FILE);
- assertThat(descriptionsIn(scope), containAll(optionSources));
+ IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+ assertThat(descriptionsIn(scope), contain("java_package", "java_outer_classname",
+ "java_multiple_files", "java_generate_equals_and_hash", "optimize_for",
+ "cc_generic_services", "java_generic_services", "py_generic_services"));
}
// syntax = "proto2";
@@ -64,11 +53,9 @@
// }
@Test public void should_provide_sources_for_native_field_option() {
NativeFieldOption option = xtext.find("ctype", NativeFieldOption.class);
- IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
- ProtoDescriptor descriptor =
- descriptorProvider.primaryDescriptor(EResources.getProjectOf(xtext.resource()));
- Collection<MessageField> optionSources = descriptor.optionsOfType(FIELD);
- assertThat(descriptionsIn(scope), containAll(optionSources));
+ IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+ assertThat(descriptionsIn(scope), contain("ctype", "packed", "deprecated",
+ "experimental_map_key"));
}
// syntax = "proto2";
@@ -84,11 +71,10 @@
// option (code) = 68;
@Test public void should_provide_sources_for_custom_option() {
Option option = xtext.find("code", ")", Option.class);
- IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
- assertThat(descriptionsIn(scope), containAll("code", "proto.code", "google.proto.code", "com.google.proto.code",
- ".com.google.proto.code",
- "info", "proto.info", "google.proto.info", "com.google.proto.info",
- ".com.google.proto.info"));
+ 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"));
}
// // Create file custom-options.proto
@@ -110,9 +96,8 @@
// option (test.proto.code) = 68;
@Test public void should_provide_imported_sources_for_custom_option() {
Option option = xtext.find("code", ")", Option.class);
- IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
- assertThat(descriptionsIn(scope), containAll("test.proto.code", ".test.proto.code",
- "test.proto.info", ".test.proto.info"));
+ IScope scope = scopeProvider.getScope(option.getSource(), OPTION_SOURCE__TARGET);
+ assertThat(descriptionsIn(scope), contain("test.proto.code", "test.proto.info"));
}
// // Create file custom-options.proto
@@ -134,11 +119,10 @@
// option (code) = 68;
@Test public void should_provide_imported_sources_for_custom_option_with_equal_package() {
Option option = xtext.find("code", ")", Option.class);
- IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
- assertThat(descriptionsIn(scope), containAll("code", "proto.code", "google.proto.code", "com.google.proto.code",
- ".com.google.proto.code",
- "info", "proto.info", "google.proto.info", "com.google.proto.info",
- ".com.google.proto.info"));
+ 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"));
}
// syntax = "proto2";
@@ -155,10 +139,9 @@
// }
@Test public void should_provide_sources_for_custom_field_option() {
CustomFieldOption option = xtext.find("code", ")", CustomFieldOption.class);
- IScope scope = scopeProvider.scope_OptionSource_target(option.getSource(), reference);
- assertThat(descriptionsIn(scope), containAll("code", "proto.code", "google.proto.code", "com.google.proto.code",
- ".com.google.proto.code",
- "info", "proto.info", "google.proto.info", "com.google.proto.info",
- ".com.google.proto.info"));
+ 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"));
}
}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
index 378fe86..d793075 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
@@ -15,6 +15,7 @@
import com.google.eclipse.protobuf.resource.FastXtextResourceSet;
import com.google.eclipse.protobuf.resource.GlobalResourceServiceProvider;
import com.google.eclipse.protobuf.scoping.ExtensionRegistryProvider;
+import com.google.eclipse.protobuf.scoping.ProtobufCaseInsensitivityHelper;
import com.google.eclipse.protobuf.validation.ProtobufResourceValidator;
import com.google.eclipse.protobuf.validation.ProtobufSyntaxErrorMessageProvider;
import com.google.inject.Binder;
@@ -27,6 +28,7 @@
import org.eclipse.xtext.resource.IGlobalServiceProvider;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.resource.XtextResourceSet;
+import org.eclipse.xtext.scoping.ICaseInsensitivityHelper;
import org.eclipse.xtext.validation.IResourceValidator;
/**
@@ -42,6 +44,10 @@
return ProtobufQualifiedNameConverter.class;
}
+ public Class<? extends ICaseInsensitivityHelper> bindICaseInsensitivityHelper() {
+ return ProtobufCaseInsensitivityHelper.class;
+ }
+
@Override
public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
return ProtobufQualifiedNameProvider.class;
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/QualifiedNames.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/QualifiedNames.java
index a7402ad..b7708de 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/QualifiedNames.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/QualifiedNames.java
@@ -8,8 +8,7 @@
*/
package com.google.eclipse.protobuf.model.util;
-import static com.google.common.collect.Lists.newArrayList;
-
+import java.util.ArrayList;
import java.util.List;
import org.eclipse.xtext.naming.QualifiedName;
@@ -23,14 +22,23 @@
*
* @author alruiz@google.com (Alex Ruiz)
*/
-@Singleton public class QualifiedNames {
+@Singleton
+public class QualifiedNames {
@Inject private StringLists stringLists;
+ public static QualifiedName removeLeadingDot(QualifiedName name) {
+ String firstSegment = name.getFirstSegment();
+ if (firstSegment != null && firstSegment.isEmpty()) {
+ return name.skipFirst(1);
+ }
+ return name;
+ }
+
public QualifiedName addLeadingDot(QualifiedName name) {
- if (name.getFirstSegment().equals("")) {
+ if (name.getFirstSegment().isEmpty()) {
return name;
}
- List<String> segments = newArrayList();
+ List<String> segments = new ArrayList<>();
segments.addAll(name.getSegments());
segments.add(0, "");
return createQualifiedName(segments);
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufCaseInsensitivityHelper.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufCaseInsensitivityHelper.java
new file mode 100644
index 0000000..34027fd
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufCaseInsensitivityHelper.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.scoping;
+
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.xtext.scoping.impl.CaseInsensitivityHelper;
+
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
+
+/**
+ * Specify cases where scoping is case insensitive.
+ *
+ * @author atrookey@google.com (Alexander Rookey)
+ */
+public class ProtobufCaseInsensitivityHelper extends CaseInsensitivityHelper {
+ @Override
+ public boolean isIgnoreCase(EReference reference) {
+ if (reference.equals(OPTION_SOURCE__TARGET)) {
+ return true;
+ }
+ return super.isIgnoreCase(reference);
+ }
+}
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 fcc0476..15f9a2c 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
@@ -8,7 +8,7 @@
*/
package com.google.eclipse.protobuf.scoping;
-import static java.util.Collections.emptyList;
+import static com.google.eclipse.protobuf.model.util.QualifiedNames.removeLeadingDot;
import java.util.ArrayList;
import java.util.List;
@@ -22,10 +22,18 @@
import org.eclipse.xtext.scoping.impl.ImportNormalizer;
import org.eclipse.xtext.scoping.impl.ImportScope;
-import com.google.common.collect.Lists;
+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 after initialization.
+ *
+ * @author (atrookey@google.com) Alexander Rookey
+ */
public class ProtobufImportScope extends ImportScope {
private final EClass type;
+ private List<ImportNormalizer> normalizers;
public ProtobufImportScope(
List<ImportNormalizer> namespaceResolvers,
@@ -35,46 +43,74 @@
boolean ignoreCase) {
super(namespaceResolvers, parent, importFrom, type, ignoreCase);
this.type = type;
+ this.normalizers = removeDuplicates(namespaceResolvers);
}
@Override
protected Iterable<IEObjectDescription> getAliasedElements(
Iterable<IEObjectDescription> candidates) {
- ArrayList<IEObjectDescription> descriptions =
- Lists.newArrayList(super.getAliasedElements(candidates));
+ Multimap<QualifiedName, IEObjectDescription> keyToDescription = LinkedHashMultimap.create();
+ Multimap<QualifiedName, ImportNormalizer> keyToNormalizer = HashMultimap.create();
+
for (IEObjectDescription imported : candidates) {
- descriptions.add(new AliasedEObjectDescription(addLeadingDot(imported.getName()), imported));
+ 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);
+ }
+ }
}
- return descriptions;
+ for (QualifiedName name : keyToNormalizer.keySet()) {
+ if (keyToNormalizer.get(name).size() > 1) keyToDescription.removeAll(name);
+ }
+ return keyToDescription.values();
}
+ // TODO (atrookey) Refactor this method for clarity
@Override
protected Iterable<IEObjectDescription> getLocalElementsByName(QualifiedName name) {
- List<IEObjectDescription> result =
- (List<IEObjectDescription>) super.getLocalElementsByName(name);
+ List<IEObjectDescription> result = new ArrayList<>();
QualifiedName resolvedQualifiedName = null;
- final QualifiedName resolvedName = name.skipFirst(1);
ISelectable importFrom = getImportFrom();
- 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 emptyList();
+ for (ImportNormalizer normalizer : normalizers) {
+ 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()) {
+ continue;
+ }
}
+ QualifiedName alias = normalizer.deresolve(resolvedElement.getName());
+ if (alias == null)
+ throw new IllegalStateException(
+ "Couldn't deresolve " + resolvedElement.getName() + " with import " + normalizer);
+ final AliasedEObjectDescription aliasedEObjectDescription =
+ new AliasedEObjectDescription(alias, resolvedElement);
+ result.add(aliasedEObjectDescription);
}
- QualifiedName alias = addLeadingDot(resolvedElement.getName());
- final AliasedEObjectDescription aliasedEObjectDescription =
- new AliasedEObjectDescription(alias, resolvedElement);
- result.add(aliasedEObjectDescription);
}
}
return result;
}
- private QualifiedName addLeadingDot(QualifiedName qualifiedName) {
- return QualifiedName.create("").append(qualifiedName);
+ /** Before getting element, check for and remove leading dot. */
+ @Override
+ public IEObjectDescription getSingleElement(QualifiedName name) {
+ return super.getSingleElement(removeLeadingDot(name));
+ }
+
+ public void addNormalizer(ImportNormalizer normalizer) {
+ normalizers.add(normalizer);
}
}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriGlobalScopeProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriGlobalScopeProvider.java
index df27228..0edb1a1 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriGlobalScopeProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriGlobalScopeProvider.java
@@ -8,6 +8,7 @@
*/
package com.google.eclipse.protobuf.scoping;
+import com.google.common.base.Predicate;
import com.google.eclipse.protobuf.model.util.Imports;
import com.google.eclipse.protobuf.model.util.Protobufs;
import com.google.eclipse.protobuf.model.util.Resources;
@@ -19,7 +20,12 @@
import org.eclipse.core.resources.IProject;
import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.xtext.resource.IEObjectDescription;
+import org.eclipse.xtext.resource.IResourceDescription;
+import org.eclipse.xtext.resource.IResourceDescriptions;
+import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
import org.eclipse.xtext.util.IResourceScopeCache;
@@ -30,6 +36,18 @@
* caches the result.
*/
public class ProtobufImportUriGlobalScopeProvider extends ImportUriGlobalScopeProvider {
+ @Override
+ protected IScope createLazyResourceScope(
+ IScope parent,
+ URI uri,
+ IResourceDescriptions descriptions,
+ EClass type,
+ Predicate<IEObjectDescription> filter,
+ boolean ignoreCase) {
+ IResourceDescription description = descriptions.getResourceDescription(uri);
+ return ProtobufSelectableBasedScope.createScope(parent, description, filter, type, ignoreCase);
+ }
+
@Inject private Protobufs protobufs;
@Inject private Resources resources;
@Inject private Imports imports;
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 76cd4ab..9767067 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,6 +8,8 @@
*/
package com.google.eclipse.protobuf.scoping;
+import static java.util.Collections.singletonList;
+
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
@@ -15,6 +17,8 @@
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;
+import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.ISelectable;
import org.eclipse.xtext.scoping.IScope;
@@ -26,10 +30,18 @@
import com.google.eclipse.protobuf.naming.ProtobufQualifiedNameConverter;
import com.google.inject.Inject;
+/**
+ * A local scope provider for the Protobuf language that
+ * understands namespace imports.
+ *
+ * @author (atrookey@google.com) Alexander Rookey
+ */
public class ProtobufImportedNamespaceAwareLocalScopeProvider
extends ImportedNamespaceAwareLocalScopeProvider {
@Inject private ProtobufQualifiedNameConverter qualifiedNameConverter;
+ private static final boolean WILDCARD = true;
+
@Override
protected ImportScope createImportScope(
IScope parent,
@@ -46,22 +58,22 @@
List<ImportNormalizer> importedNamespaceResolvers = new ArrayList<>();
EList<EObject> eContents = context.eContents();
for (EObject child : eContents) {
- String namespace = getImportedNamespace(child);
- if (namespace != null) {
- ImportNormalizer resolver = createImportedNamespaceResolver(namespace, ignoreCase);
+ String name = getImportedNamespace(child);
+ if (name != null && !name.isEmpty()) {
+ ImportNormalizer resolver = createImportedNamespaceResolver(name, ignoreCase);
if (resolver != null) {
importedNamespaceResolvers.add(resolver);
}
- importedNamespaceResolvers.addAll(createResolversForInnerNamespaces(namespace, ignoreCase));
+ 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:
+ * 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.*}
@@ -88,14 +100,7 @@
return importedNamespaceResolvers;
}
- /**
- * Creates a new {@link ImportNormalizer} for the given namespace.
- *
- * @param namespace the namespace.
- * @param ignoreCase {@code true} if the resolver should be case insensitive.
- * @return a new {@link ImportNormalizer} or {@code null} if the namespace
- * cannot be converted to a valid qualified name.
- */
+ /** Creates an {@link ImportNormalizer} with wildcards. */
@Override
protected ImportNormalizer createImportedNamespaceResolver(String namespace, boolean ignoreCase) {
if (Strings.isEmpty(namespace)) {
@@ -105,6 +110,84 @@
if (importedNamespace == null || importedNamespace.isEmpty()) {
return null;
}
- return doCreateImportNormalizer(importedNamespace, true, ignoreCase);
+ return doCreateImportNormalizer(importedNamespace, WILDCARD, ignoreCase);
+ }
+
+ /**
+ * Creates a {@link ProtobufImportScope} regardless of whether or not
+ * {@code namespaceResolvers} is empty.
+ */
+ @Override
+ protected IScope getLocalElementsScope(
+ IScope parent, final EObject context, final 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 (isRelativeImport() && name != null && !name.isEmpty()) {
+ ImportNormalizer localNormalizer = doCreateImportNormalizer(name, true, ignoreCase);
+ result =
+ createImportScope(
+ result,
+ singletonList(localNormalizer),
+ allDescriptions,
+ reference.getEReferenceType(),
+ isIgnoreCase(reference));
+ }
+ result =
+ createImportScope(
+ result,
+ namespaceResolvers,
+ null,
+ reference.getEReferenceType(),
+ isIgnoreCase(reference));
+ if (name != null) {
+ ImportNormalizer localNormalizer = doCreateImportNormalizer(name, true, ignoreCase);
+ result =
+ createImportScope(
+ result,
+ singletonList(localNormalizer),
+ allDescriptions,
+ reference.getEReferenceType(),
+ isIgnoreCase(reference));
+ }
+ return result;
+ }
+
+ /**
+ * Makes {@code getAllDescriptions()} visible to {@link ProtobufScopeProvider}
+ */
+ @Override
+ protected ISelectable getAllDescriptions(Resource resource) {
+ return super.getAllDescriptions(resource);
+ }
+ /**
+ * Makes {@code getImportedNamespaceResolvers()} visible to
+ * {@link ProtobufScopeProvider}
+ */
+ @Override
+ protected List<ImportNormalizer> getImportedNamespaceResolvers(
+ EObject context, boolean ignoreCase) {
+ return super.getImportedNamespaceResolvers(context, ignoreCase);
+ }
+ /**
+ * Makes {@code getResourceScope()} visible to {@link ProtobufScopeProvider}
+ */
+ @Override
+ protected IScope getResourceScope(Resource res, EReference reference) {
+ return super.getResourceScope(res, reference);
+ }
+
+ /** Returns a {@link ProtobufSelectableBasedScope} instead of {@link SelectableBasedScope} */
+ @Override
+ protected IScope getResourceScope(IScope parent, EObject context, EReference reference) {
+ if (context.eResource() == null) {
+ return parent;
+ }
+ ISelectable allDescriptions = getAllDescriptions(context.eResource());
+ return ProtobufSelectableBasedScope.createScope(
+ parent, allDescriptions, reference.getEReferenceType(), isIgnoreCase(reference));
}
}
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 f34a85a..d541014 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
@@ -8,80 +8,209 @@
*/
package com.google.eclipse.protobuf.scoping;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION_SOURCE__TARGET;
import static com.google.eclipse.protobuf.scoping.OptionType.typeOf;
+import static com.google.eclipse.protobuf.util.Encodings.UTF_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.emptySet;
+import static java.util.Collections.singletonMap;
+import static org.eclipse.emf.ecore.resource.ContentHandler.UNSPECIFIED_CONTENT_TYPE;
+import static org.eclipse.xtext.EcoreUtil2.resolveLazyCrossReferences;
+import static org.eclipse.xtext.resource.XtextResource.OPTION_ENCODING;
+import static org.eclipse.xtext.util.CancelIndicator.NullImpl;
-import com.google.eclipse.protobuf.model.util.MessageFields;
import com.google.eclipse.protobuf.model.util.ModelObjects;
-import com.google.eclipse.protobuf.model.util.Options;
+import com.google.eclipse.protobuf.naming.NameResolver;
+import com.google.eclipse.protobuf.naming.ProtobufQualifiedNameConverter;
+import com.google.eclipse.protobuf.preferences.general.PreferenceNames;
import com.google.eclipse.protobuf.protobuf.AbstractCustomOption;
import com.google.eclipse.protobuf.protobuf.AbstractOption;
import com.google.eclipse.protobuf.protobuf.ComplexType;
+import com.google.eclipse.protobuf.protobuf.ComplexTypeLink;
import com.google.eclipse.protobuf.protobuf.ComplexValue;
-import com.google.eclipse.protobuf.protobuf.DefaultValueFieldOption;
+import com.google.eclipse.protobuf.protobuf.ComplexValueField;
+import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
+import com.google.eclipse.protobuf.protobuf.CustomOption;
import com.google.eclipse.protobuf.protobuf.Enum;
import com.google.eclipse.protobuf.protobuf.ExtensibleType;
import com.google.eclipse.protobuf.protobuf.FieldName;
+import com.google.eclipse.protobuf.protobuf.FieldOption;
import com.google.eclipse.protobuf.protobuf.Group;
-import com.google.eclipse.protobuf.protobuf.GroupElement;
-import com.google.eclipse.protobuf.protobuf.IndexedElement;
+import com.google.eclipse.protobuf.protobuf.Literal;
import com.google.eclipse.protobuf.protobuf.LiteralLink;
import com.google.eclipse.protobuf.protobuf.Message;
import com.google.eclipse.protobuf.protobuf.MessageField;
-import com.google.eclipse.protobuf.protobuf.MessageLink;
-import com.google.eclipse.protobuf.protobuf.MessageOptionField;
-import com.google.eclipse.protobuf.protobuf.NormalFieldName;
+import com.google.eclipse.protobuf.protobuf.NativeFieldOption;
+import com.google.eclipse.protobuf.protobuf.NativeOption;
+import com.google.eclipse.protobuf.protobuf.OneOf;
import com.google.eclipse.protobuf.protobuf.OptionField;
import com.google.eclipse.protobuf.protobuf.OptionSource;
import com.google.eclipse.protobuf.protobuf.Protobuf;
import com.google.eclipse.protobuf.protobuf.Rpc;
-import com.google.eclipse.protobuf.protobuf.SimpleValueField;
import com.google.eclipse.protobuf.protobuf.Stream;
import com.google.eclipse.protobuf.protobuf.TypeExtension;
+import com.google.eclipse.protobuf.protobuf.TypeLink;
+import com.google.eclipse.protobuf.protobuf.ValueField;
import com.google.eclipse.protobuf.util.EResources;
import com.google.inject.Inject;
+import com.google.inject.Provider;
+import org.apache.log4j.Level;
import org.eclipse.core.resources.IProject;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.xtext.linking.impl.LinkingHelper;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.nodemodel.ICompositeNode;
+import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.IEObjectDescription;
+import org.eclipse.xtext.resource.ISelectable;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
-import org.eclipse.xtext.scoping.impl.SimpleScope;
+import org.eclipse.xtext.scoping.impl.ImportNormalizer;
+import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
+import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
+import org.eclipse.xtext.util.IResourceScopeCache;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
+import java.util.List;
/**
- * Custom scoping description.
+ * A scope provider for the Protobuf language.
*
- * @author alruiz@google.com (Alex Ruiz)
- *
- * @see <a href="http://www.eclipse.org/Xtext/documentation/latest/xtext.html#scoping">Xtext
- * Scoping</a>
+ * @author atrookey@google.com (Alexander Rookey)
*/
public class ProtobufScopeProvider extends AbstractDeclarativeScopeProvider
implements ScopeProvider {
- private static final boolean DO_NOT_IGNORE_CASE = false;
-
@Inject private ComplexTypeFinderStrategy complexTypeFinderDelegate;
@Inject private CustomOptionFieldFinder customOptionFieldFinder;
@Inject private CustomOptionFieldNameFinder customOptionFieldNameFinder;
@Inject private CustomOptionFinderStrategy customOptionFinderDelegate;
- @Inject private ExtensionFieldNameFinderStrategy extensionFieldNameFinderDelegate;
@Inject private ExtensionFieldFinderStrategy extensionFieldFinderDelegate;
- @Inject private ProtoDescriptorProvider descriptorProvider;
+ @Inject private ExtensionFieldNameFinderStrategy extensionFieldNameFinderDelegate;
@Inject private MessageFieldFinderStrategy messageFieldFinderDelegate;
- @Inject private MessageFields messageFields;
@Inject private ModelElementFinder modelElementFinder;
@Inject private ModelObjects modelObjects;
@Inject private NormalFieldNameFinderStrategy normalFieldNameFinderDelegate;
- @Inject private LiteralDescriptions literalDescriptions;
- @Inject private NativeOptionDescriptions nativeOptionDescriptions;
- @Inject private Options options;
+ @Inject private LinkingHelper linkingHelper;
+ @Inject private IPreferenceStoreAccess storeAccess;
+ @Inject private IUriResolver uriResolver;
+ @Inject private IResourceScopeCache cache;
+ @Inject private NameResolver nameResolver;
+ @Inject private ProtobufQualifiedNameConverter nameConverter;
+
+ private static final String CACHEKEY = "IMPORT_NORMALIZER_CACHE_KEY";
+
+ /**
+ * Returns the name of the descriptor.proto message declaring default options. The options must
+ * match the option type of {@code context}. For example, if {@code context} is a file option,
+ * {@code FileOptions} should be returned because it contains the declarations of the default file
+ * options.
+ */
+ // TODO (atrookey) Create utility for getting the type of an Option.
+ private static String getOptionType(EObject context) {
+ if (context == null) {
+ return "FileOptions";
+ }
+ if (context instanceof Message) {
+ return "MessageOptions";
+ }
+ if (context instanceof Enum) {
+ return "EnumOptions";
+ }
+ if (context instanceof FieldOption) {
+ return "FieldOptions";
+ }
+ if (context instanceof Rpc) {
+ return "MethodOptions";
+ }
+ if (context instanceof Stream) {
+ return "StreamOptions";
+ }
+ if (context instanceof Literal) {
+ return "EnumValueOptions";
+ }
+ return getOptionType(context.eContainer());
+ }
+
+ private Collection<IEObjectDescription> allMessages(Protobuf root) {
+ return modelElementFinder.find(root, complexTypeFinderDelegate, Message.class);
+ }
+
+ /**
+ * Returns the InputStream associated with the resource at location {@code descriptorLocation}.
+ */
+ private InputStream openFile(URI fileLocation) throws IOException {
+ URL url = new URL(fileLocation.toString());
+ return url.openConnection().getInputStream();
+ }
+
+ // TODO (atrookey) Create utility for getting package.
+ private String getPackageOfResource(Resource resource) {
+ Protobuf object;
+ if (resource != null && (object = (Protobuf) resource.getContents().get(0)) != null) {
+ for (EObject content : object.getElements()) {
+ if (content instanceof com.google.eclipse.protobuf.protobuf.Package) {
+ return ((com.google.eclipse.protobuf.protobuf.Package) content).getImportedNamespace();
+ }
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Returns descriptor associated with the current project.
+ */
+ private @Nullable Resource getDescriptorResource(EObject context) {
+ URI descriptorLocation;
+ IProject project = EResources.getProjectOf(context.eResource());
+ IPreferenceStore store = storeAccess.getWritablePreferenceStore(project);
+ String rawDescriptorLocation = store.getString(PreferenceNames.DESCRIPTOR_PROTO_PATH);
+ descriptorLocation =
+ URI.createURI(uriResolver.resolveUri(rawDescriptorLocation, null, project));
+ ResourceSet resourceSet = context.eResource().getResourceSet();
+ Resource resource = resourceSet.getResource(descriptorLocation, true);
+ if (resource != null) {
+ return resource;
+ }
+ try {
+ InputStream contents = openFile(descriptorLocation);
+ resource = resourceSet.createResource(descriptorLocation, UNSPECIFIED_CONTENT_TYPE);
+ resource.load(contents, singletonMap(OPTION_ENCODING, UTF_8));
+ resolveLazyCrossReferences(resource, NullImpl);
+ return resource;
+ } catch (IOException e) {
+ logger.log(Level.ERROR, e);
+ }
+ return null;
+ }
+
+ /**
+ * Returns name of an object as a QualifiedName.
+ */
+ private QualifiedName getEObjectName(EObject object) {
+ ICompositeNode node = NodeModelUtils.getNode(object);
+ String name = linkingHelper.getCrossRefNodeAsString(node, true);
+ return QualifiedName.create(name);
+ }
+
+ /**
+ * Returns the local scope provider.
+ */
+ private ProtobufImportedNamespaceAwareLocalScopeProvider getLocalScopeProvider() {
+ return (ProtobufImportedNamespaceAwareLocalScopeProvider) super.getDelegate();
+ }
@Override
public IScope getScope(EObject context, EReference reference) {
@@ -95,6 +224,86 @@
return scope;
}
+ /**
+ * Recursively scope {@code FieldName} starting with an {@code OptionSource}.
+ */
+ private IScope getScopeOfFieldName(
+ IScope delegatedScope, OptionSource optionSource, EReference reference) {
+ IScope retval = IScope.NULLSCOPE;
+ IScope parentScope = super.getScope(optionSource, OPTION_SOURCE__TARGET);
+ QualifiedName optionSourceName = getEObjectName(optionSource);
+ IEObjectDescription indexedElementDescription = parentScope.getSingleElement(optionSourceName);
+ if (indexedElementDescription != null) {
+ EObject indexedElement = indexedElementDescription.getEObjectOrProxy();
+ retval = getLocalScopeOfMessageFieldOrGroup(delegatedScope, indexedElement, reference);
+ }
+ return retval;
+ }
+
+ /**
+ * Locally scope any children of {@code context} that are of type {@code OneOf}.
+ */
+ private IScope getScopeOfOneOf(IScope scope, EObject context, EReference reference) {
+ IScope result = scope;
+ for (EObject element : context.eContents()) {
+ if (element instanceof OneOf) {
+ result = getLocalScopeProvider().getLocalElementsScope(result, element, reference);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Recursively scope {@code OptionField} starting with an {@code OptionSource}.
+ */
+ private IScope getScopeOfOptionField(
+ OptionSource optionSource, EReference reference, int fieldIndex, EList<OptionField> fields) {
+ if (fieldIndex < 0 || fields.size() <= fieldIndex) {
+ throw new IllegalArgumentException();
+ }
+ IScope parentScope, retval = IScope.NULLSCOPE;
+ QualifiedName name = QualifiedName.EMPTY;
+ if (fieldIndex == 0) {
+ parentScope = super.getScope(optionSource, OPTION_SOURCE__TARGET);
+ name = getEObjectName(optionSource);
+ } else {
+ OptionField parentOptionField = fields.get(fieldIndex - 1);
+ parentScope = getScopeOfOptionField(optionSource, reference, fieldIndex - 1, fields);
+ name = getEObjectName(parentOptionField);
+ }
+ IEObjectDescription indexedElementDescription = parentScope.getSingleElement(name);
+ if (indexedElementDescription != null) {
+ EObject indexedElement = indexedElementDescription.getEObjectOrProxy();
+ retval = getLocalScopeOfMessageFieldOrGroup(retval, indexedElement, reference);
+ }
+ return retval;
+ }
+
+ /**
+ * Recursively scope nested OptionFields and FieldNames.
+ *
+ * <p>If {@code IndexedElement} refers to an {@code MessageField}, scope the {@code Message} of
+ * that field. If it refers to a {@code Group}, return the scope of that {@code Group}.
+ */
+ private IScope getLocalScopeOfMessageFieldOrGroup(
+ IScope parentScope, EObject indexedElement, EReference reference) {
+ IScope retval = IScope.NULLSCOPE;
+ if (indexedElement instanceof MessageField) {
+ TypeLink typeLink = ((MessageField) indexedElement).getType();
+ if (typeLink instanceof ComplexTypeLink) {
+ ComplexType complexType = ((ComplexTypeLink) typeLink).getTarget();
+ IScope result =
+ getLocalScopeProvider().getLocalElementsScope(parentScope, complexType, reference);
+ retval = getScopeOfOneOf(result, complexType, reference);
+ }
+ }
+ if (indexedElement instanceof Group) {
+ retval =
+ getLocalScopeProvider().getLocalElementsScope(parentScope, indexedElement, reference);
+ }
+ return retval;
+ }
+
@Override
public Collection<IEObjectDescription> potentialComplexTypesFor(MessageField field) {
return modelElementFinder.find(field, complexTypeFinderDelegate, ComplexType.class);
@@ -106,11 +315,20 @@
return modelElementFinder.find(root, complexTypeFinderDelegate, ExtensibleType.class);
}
- @SuppressWarnings("unused")
- public IScope scope_MessageLink_target(MessageLink link, EReference r) {
- Protobuf root = modelObjects.rootOf(link);
- Collection<IEObjectDescription> messages = allMessages(root);
- return createScope(messages);
+ @Override
+ public Collection<IEObjectDescription> potentialExtensionFieldNames(ComplexValue value) {
+ return customOptionFieldNameFinder.findFieldNamesSources(
+ value, extensionFieldNameFinderDelegate);
+ }
+
+ @Override
+ public Collection<IEObjectDescription> potentialExtensionFieldsFor(AbstractCustomOption option) {
+ return customOptionFieldFinder.findOptionFields(option, extensionFieldFinderDelegate);
+ }
+
+ @Override
+ public Collection<IEObjectDescription> potentialMessageFieldsFor(AbstractCustomOption option) {
+ return customOptionFieldFinder.findOptionFields(option, messageFieldFinderDelegate);
}
@Override
@@ -125,67 +343,9 @@
return allMessages(root);
}
- private Collection<IEObjectDescription> allMessages(Protobuf root) {
- return modelElementFinder.find(root, complexTypeFinderDelegate, Message.class);
- }
-
- @SuppressWarnings("unused")
- public IScope scope_LiteralLink_target(LiteralLink link, EReference r) {
- EObject container = link.eContainer();
- Enum anEnum = null;
- if (container instanceof DefaultValueFieldOption) {
- container = container.eContainer();
- }
- if (container instanceof AbstractOption) {
- AbstractOption option = (AbstractOption) container;
- if (options.isNative(option)) {
- IProject project = EResources.getProjectOf(option.eResource());
- ProtoDescriptor descriptor = descriptorProvider.primaryDescriptor(project);
- IndexedElement e = options.rootSourceOf(option);
- anEnum = descriptor.enumTypeOf((MessageField) e);
- }
- }
- if (container instanceof AbstractCustomOption) {
- AbstractCustomOption option = (AbstractCustomOption) container;
- container = options.sourceOf(option);
- }
- if (container instanceof SimpleValueField) {
- SimpleValueField field = (SimpleValueField) container;
- container = field.getName().getTarget();
- }
- if (container instanceof MessageField) {
- anEnum = messageFields.enumTypeOf((MessageField) container);
- }
- return createScope(literalDescriptions.literalsOf(anEnum));
- }
-
- @SuppressWarnings("unused")
- public IScope scope_OptionSource_target(OptionSource source, EReference r) {
- EObject c = source.eContainer();
- if (c instanceof AbstractOption) {
- AbstractOption option = (AbstractOption) c;
- if (options.isNative(option)) {
- return createScope(nativeOptionDescriptions.sources(option));
- }
- }
- if (c instanceof AbstractCustomOption) {
- AbstractCustomOption option = (AbstractCustomOption) c;
-
- if (c instanceof GroupElement) {
- EObject container = c.eContainer();
- if (container instanceof Group) {
- OptionType optionType = OptionType.findOptionTypeForLevelOf(container.eContainer());
- return createScope(
- optionType != null
- ? modelElementFinder.find(option, customOptionFinderDelegate, optionType)
- : Collections.<IEObjectDescription>emptySet());
- }
- }
-
- return createScope(potentialSourcesFor(option));
- }
- Set<IEObjectDescription> descriptions = emptySet();
- return createScope(descriptions);
+ @Override
+ public Collection<IEObjectDescription> potentialNormalFieldNames(ComplexValue value) {
+ return customOptionFieldNameFinder.findFieldNamesSources(value, normalFieldNameFinderDelegate);
}
@Override
@@ -198,75 +358,234 @@
return descriptions;
}
- @SuppressWarnings("unused")
- public IScope scope_OptionField_target(OptionField field, EReference r) {
- return createScope(allPossibleSourcesOf(field));
- }
-
- private Collection<IEObjectDescription> allPossibleSourcesOf(OptionField field) {
- if (field == null) {
- return emptySet();
- }
- EObject container = field.eContainer();
- if (container instanceof AbstractCustomOption) {
- AbstractCustomOption option = (AbstractCustomOption) container;
- if (field instanceof MessageOptionField) {
- return customOptionFieldFinder.findOptionFields(option, messageFieldFinderDelegate, field);
+ /**
+ * Recursively scopes the {@code FieldName} starting with the {@code OptionSource}.
+ *
+ * For example:
+ *
+ * <pre>
+ * message FooOptions {
+ * optional int32 opt1 = 1;
+ * }
+ * extend google.protobuf.FieldOptions {
+ * optional FooOptions foo_options = 1234;
+ * }
+ * message Bar {
+ * optional int32 b = 1 [(foo_options) = { opt1: 123 }];
+ * }
+ * </pre>
+ *
+ * The {@code NormalFieldName} {@code opt1} contains a cross-reference to {@code FooOptions.opt1}.
+ */
+ public IScope scope_FieldName_target(FieldName fieldName, EReference reference) {
+ OptionSource optionSource = null;
+ EObject valueField = fieldName.eContainer();
+ if (valueField != null && valueField instanceof ValueField) {
+ EObject complexValue = valueField.eContainer();
+ if (complexValue != null && complexValue instanceof ComplexValue) {
+ EObject unknownOption = complexValue.eContainer();
+ IScope delegatedScope = super.delegateGetScope(fieldName, reference);
+ if (unknownOption != null && unknownOption instanceof ComplexValueField) {
+ ComplexValueField complexValueField = (ComplexValueField) unknownOption;
+ IScope parentScope = scope_FieldName_target(complexValueField.getName(), reference);
+ IEObjectDescription indexedElementDescription =
+ parentScope.getSingleElement(this.getEObjectName(complexValueField.getName()));
+ // TODO (atrookey) This is only necessary because of ExtensionFieldName.
+ // Could be made more specific.
+ return getLocalScopeOfMessageFieldOrGroup(
+ delegatedScope, indexedElementDescription.getEObjectOrProxy(), reference);
+ }
+ if (unknownOption != null && unknownOption instanceof NativeFieldOption) {
+ NativeFieldOption nativeFieldOption = (NativeFieldOption) unknownOption;
+ optionSource = nativeFieldOption.getSource();
+ }
+ if (unknownOption != null && unknownOption instanceof CustomFieldOption) {
+ CustomFieldOption customFieldOption = (CustomFieldOption) unknownOption;
+ optionSource = customFieldOption.getSource();
+ }
+ if (unknownOption != null && unknownOption instanceof NativeOption) {
+ NativeOption option = (NativeOption) unknownOption;
+ optionSource = option.getSource();
+ }
+ if (unknownOption != null && unknownOption instanceof CustomOption) {
+ CustomOption option = (CustomOption) unknownOption;
+ optionSource = option.getSource();
+ }
+ return getScopeOfFieldName(delegatedScope, optionSource, reference);
}
- return customOptionFieldFinder.findOptionFields(option, extensionFieldFinderDelegate, field);
- }
- return emptySet();
- }
-
- @Override
- public Collection<IEObjectDescription> potentialMessageFieldsFor(AbstractCustomOption option) {
- return customOptionFieldFinder.findOptionFields(option, messageFieldFinderDelegate);
- }
-
- @Override
- public Collection<IEObjectDescription> potentialExtensionFieldsFor(AbstractCustomOption option) {
- return customOptionFieldFinder.findOptionFields(option, extensionFieldFinderDelegate);
- }
-
- @SuppressWarnings("unused")
- public IScope scope_FieldName_target(FieldName name, EReference r) {
- return createScope(findSources(name));
- }
-
- private Collection<IEObjectDescription> findSources(FieldName name) {
- ComplexValue value = container(name);
- if (value == null) {
- return emptySet();
- }
- if (name instanceof NormalFieldName) {
- return potentialNormalFieldNames(value);
- }
- return potentialExtensionFieldNames(value);
- }
-
- private ComplexValue container(FieldName name) {
- EObject container = name;
- while (container != null) {
- if (container instanceof ComplexValue) {
- return (ComplexValue) container;
- }
- container = container.eContainer();
}
return null;
}
- @Override
- public Collection<IEObjectDescription> potentialNormalFieldNames(ComplexValue value) {
- return customOptionFieldNameFinder.findFieldNamesSources(value, normalFieldNameFinderDelegate);
+ /**
+ * Creates a scope containing elements of type {@code Literal} that can be
+ * referenced with their local name only.
+ *
+ * For example:
+ *
+ * <pre>
+ * enum MyEnum {
+ * FOO = 1;
+ * }
+ * extend google.protobuf.ServiceOptions {
+ * optional MyEnum my_service_option = 50005;
+ * }
+ * service MyService {
+ * option (my_service_option) = FOO;
+ * }
+ * </pre>
+ *
+ * The {@code LiteralLink} {@code FOO} contains a cross-reference to {@code MyEnum.FOO}.
+ */
+ public IScope scope_LiteralLink_target(LiteralLink literalLink, EReference reference) {
+ IScope scope = IScope.NULLSCOPE;
+ scope =
+ createLiteralLinkResolvedScopeForResource(
+ reference, scope, getDescriptorResource(literalLink));
+ scope = createLiteralLinkResolvedScopeForResource(reference, scope, literalLink.eResource());
+ return scope;
}
- @Override
- public Collection<IEObjectDescription> potentialExtensionFieldNames(ComplexValue value) {
- return customOptionFieldNameFinder.findFieldNamesSources(
- value, extensionFieldNameFinderDelegate);
+ // TODO (atrookey) Create resolvers for ProtobufImportScopes at all scope levels,
+ // not just top level.
+ private IScope createLiteralLinkResolvedScopeForResource(
+ EReference reference, IScope parent, Resource resource) {
+ IScope scope = parent;
+ scope = getProtobufImportScope(scope, resource, reference);
+ List<ImportNormalizer> protoNormalizers =
+ cache.get(
+ CACHEKEY,
+ resource,
+ new Provider<List<ImportNormalizer>>() {
+ @Override
+ public List<ImportNormalizer> get() {
+ return createEnumElementResolvers(
+ resource.getContents().get(0), getPackageOfResource(resource), false);
+ }
+ });
+ if (scope instanceof ProtobufImportScope) {
+ for (ImportNormalizer normalizer : protoNormalizers) {
+ ((ProtobufImportScope) scope).addNormalizer(normalizer);
+ }
+ } else if (scope instanceof SelectableBasedScope) {
+ ISelectable allDescriptions = getLocalScopeProvider().getAllDescriptions(resource);
+ scope =
+ getLocalScopeProvider()
+ .createImportScope(
+ scope, protoNormalizers, allDescriptions, reference.getEReferenceType(), false);
+ }
+ return scope;
}
- private static IScope createScope(Iterable<IEObjectDescription> descriptions) {
- return new SimpleScope(descriptions, DO_NOT_IGNORE_CASE);
+ /**
+ * Creates an {@code ImportNormalizer} for every {@code Enum} that is a descendant of {@code context}.
+ */
+ private List<ImportNormalizer> createEnumElementResolvers(
+ EObject context, String qualifiedName, boolean ignoreCase) {
+ List<ImportNormalizer> importedNamespaceResolvers = new ArrayList<>();
+ for (EObject child : context.eContents()) {
+ if (child instanceof Enum) {
+ String name = appendNameOfEObject(qualifiedName, child);
+ if (!name.isEmpty()) {
+ ImportNormalizer resolver =
+ getLocalScopeProvider().createImportedNamespaceResolver(name, ignoreCase);
+ if (resolver != null) {
+ importedNamespaceResolvers.add(resolver);
+ }
+ }
+ }
+ if (child instanceof Message) {
+ String name = appendNameOfEObject(qualifiedName, child);
+ importedNamespaceResolvers.addAll(createEnumElementResolvers(child, name, ignoreCase));
+ }
+ }
+ return importedNamespaceResolvers;
+ }
+
+ private String appendNameOfEObject(String qualifiedName, EObject child) {
+ String childName = nameResolver.nameOf(child);
+ if (qualifiedName.isEmpty()) {
+ return childName;
+ }
+ return qualifiedName + nameConverter.getDelimiter() + childName;
+ }
+
+ /**
+ * Recursively scopes the {@code OptionField} starting with the {@code OptionSource}.
+ *
+ * For example:
+ *
+ * <pre>
+ * message Code {
+ * optional double number = 1;
+ * }
+ * message Type {
+ * optional Code code = 1;
+ * }
+ * extend proto2.FieldOptions {
+ * optional Type type = 1000;
+ * }
+ * message Person {
+ * optional bool active = 1 [(type).code.number = 68];
+ * }
+ * </pre>
+ *
+ * The {@code OptionField} {@code number} contains a cross-reference to {@code Code.number}.
+ */
+ public @Nullable IScope scope_OptionField_target(OptionField optionField, EReference reference) {
+ EObject customOption = optionField.eContainer();
+ if (customOption != null) {
+ OptionSource optionSource = null;
+ EList<OptionField> fields = null;
+ if (customOption instanceof CustomFieldOption) {
+ optionSource = ((CustomFieldOption) customOption).getSource();
+ fields = ((CustomFieldOption) customOption).getFields();
+ }
+ if (customOption instanceof CustomOption) {
+ optionSource = ((CustomOption) customOption).getSource();
+ fields = ((CustomOption) customOption).getFields();
+ }
+ if (optionSource != null && fields != null) {
+ int index = fields.indexOf(optionField);
+ return getScopeOfOptionField(optionSource, reference, index, fields);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Creates a scope containing the default options defined in descriptor.proto.
+ *
+ * For example:
+ *
+ * <pre>
+ * option java_package = "com.example.foo";
+ * </pre>
+ *
+ * The {@code OptionSource} {@code java_package} contains a cross-reference to {@code
+ * google.protobuf.FileOptions.java_package} defined in descriptor.proto.
+ */
+ public IScope scope_OptionSource_target(OptionSource optionSource, EReference reference) {
+ Resource descriptorResource = getDescriptorResource(optionSource);
+ String descriptorMessage =
+ getPackageOfResource(descriptorResource)
+ + nameConverter.getDelimiter()
+ + getOptionType(optionSource);
+ ImportNormalizer normalizer =
+ getLocalScopeProvider().createImportedNamespaceResolver(descriptorMessage, false);
+ IScope scope = delegateGetScope(optionSource, reference);
+ scope = getProtobufImportScope(scope, getDescriptorResource(optionSource), reference);
+ ((ProtobufImportScope) scope).addNormalizer(normalizer);
+ return scope;
+ }
+
+ /** Returns the top level scope of the {@code Resource}. */
+ private ProtobufImportScope getProtobufImportScope(
+ IScope parent, Resource resource, EReference reference) {
+ EObject protobuf = resource.getContents().get(0);
+ IScope descriptorResourceScope =
+ getLocalScopeProvider().getResourceScope(parent, protobuf, reference);
+ return (ProtobufImportScope)
+ getLocalScopeProvider().getLocalElementsScope(descriptorResourceScope, protobuf, reference);
}
}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufSelectableBasedScope.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufSelectableBasedScope.java
new file mode 100644
index 0000000..40f6e00
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufSelectableBasedScope.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.scoping;
+
+import static com.google.eclipse.protobuf.model.util.QualifiedNames.removeLeadingDot;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.resource.IEObjectDescription;
+import org.eclipse.xtext.resource.ISelectable;
+import org.eclipse.xtext.scoping.IScope;
+import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
+
+import com.google.common.base.Predicate;
+
+/**
+ * {@link SelectableBasedScope} that handles qualified names with a leading dot.
+ *
+ * @author (atrookey@google.com) Alexander Rookey
+ */
+public class ProtobufSelectableBasedScope extends SelectableBasedScope {
+ public static IScope createScope(
+ IScope outer, ISelectable selectable, EClass type, boolean ignoreCase) {
+ return createScope(outer, selectable, null, type, ignoreCase);
+ }
+
+ public static IScope createScope(
+ IScope outer,
+ ISelectable selectable,
+ Predicate<IEObjectDescription> filter,
+ EClass type,
+ boolean ignoreCase) {
+ if (selectable == null || selectable.isEmpty()) {
+ return outer;
+ }
+ return new ProtobufSelectableBasedScope(outer, selectable, filter, type, ignoreCase);
+ }
+
+ protected ProtobufSelectableBasedScope(
+ IScope outer,
+ ISelectable selectable,
+ Predicate<IEObjectDescription> filter,
+ EClass type,
+ boolean ignoreCase) {
+ super(outer, selectable, filter, type, ignoreCase);
+ }
+
+ /** Before getting element, removes leading dot. */
+ @Override
+ public IEObjectDescription getSingleElement(QualifiedName name) {
+ return super.getSingleElement(removeLeadingDot(name));
+ }
+}