Rewrote scope_LiteralLink_target() to correctly handle enums.

The editor now narrows the scope to the type of the MessageField the
enum is being applied to.  Also handles enums from imported protos.

Change-Id: Iee211a40670d82aa874cddbaecf543aa3b76aecf
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 0f703dc..a341d0b 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
@@ -179,7 +179,7 @@
   // syntax = "proto2";
   // package sample.proto;
   //
-  // message Example {
+  // message Sample {
   //   enum Alpha {
   //     BAR =  1;
   //   }
@@ -189,20 +189,19 @@
   //  package example.proto;
   //  import "sample_proto.proto";
   //
-  // message Fruits {
-  //  enum Beta {
+  // message Example {
+  //  enum Alpha {
   //     BAR =  1;
   //  }
-  //   optional Grape grape = 4 [default = BAR];
+  //   optional Alpha alpha = 1 [default = BAR];
   // }
   @Test public void should_provide_Literal_nearest_to_option() {
-    MessageField field = xtext.find("grape", " =", MessageField.class);
+    MessageField field = xtext.find("alpha", " =", MessageField.class);
     DefaultValueFieldOption option = (DefaultValueFieldOption) field.getFieldOptions().get(0);
     LiteralLink link = (LiteralLink) option.getValue();
     Enum enumBeta = (Enum)link.getTarget().eContainer();
-    Enum beta = xtext.find("Beta", Enum.class);
-    Literal literal = (Literal) beta.getElements().get(0);
-    Enum expectedEnum = (Enum) literal.eContainer();
+    Message example = xtext.find("Example", Message.class);
+    Enum expectedEnum = (Enum) example.getElements().get(0);
     assertEquals(expectedEnum, enumBeta);
   }
 
@@ -221,7 +220,7 @@
   //   }
   //   required Status status = 1 [default = SUCCESS];
   // }
-  @Test public void should_provide_Literals_in_nearest_scope_to_default_value_field_option() {
+  @Test public void should_provide_default_Literals_matching_type_of_field() {
     MessageField field = xtext.find("status", " =", MessageField.class);
     DefaultValueFieldOption option = (DefaultValueFieldOption) field.getFieldOptions().get(0);
     LiteralLink link = (LiteralLink) option.getValue();
@@ -255,6 +254,28 @@
     assertThat(descriptionsIn(scope), contain("SUCCESS"));
   }
 
+  // // Create file sample_proto.proto
+  // syntax = "proto2";
+  // package example;
+  //
+  // enum Status {
+  //   SUCCESS = 1;
+  // }
+
+  // syntax = "proto2";
+  // package example;
+  // import "sample_proto.proto";
+  //
+  // message Response {
+  //   required Status status = 1 [default = SUCCESS];
+  // }
+  @Test public void should_provide_Literals_from_imported_proto() {
+    MessageField field = xtext.find("status", " =", MessageField.class);
+    DefaultValueFieldOption option = (DefaultValueFieldOption) field.getFieldOptions().get(0);
+    IScope scope = scopeProvider.getScope(valueOf(option), LITERAL_LINK__TARGET);
+    assertThat(descriptionsIn(scope), contain("SUCCESS"));
+  }
+
   private static LiteralLink valueOf(FieldOption option) {
     return (LiteralLink) option.getValue();
   }
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 f3cbf24..0b1fbc0 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
@@ -25,7 +25,6 @@
 import org.eclipse.xtext.scoping.IScope;
 import org.eclipse.xtext.scoping.impl.ImportNormalizer;
 import org.eclipse.xtext.scoping.impl.ImportScope;
-import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
 import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider;
 import org.eclipse.xtext.util.Strings;
 
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 aa27afe..d8d2522 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
@@ -32,11 +32,13 @@
 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.DefaultValueFieldOption;
 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.IndexedElement;
 import com.google.eclipse.protobuf.protobuf.Literal;
 import com.google.eclipse.protobuf.protobuf.LiteralLink;
 import com.google.eclipse.protobuf.protobuf.Message;
@@ -44,6 +46,7 @@
 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.Option;
 import com.google.eclipse.protobuf.protobuf.OptionField;
 import com.google.eclipse.protobuf.protobuf.OptionSource;
 import com.google.eclipse.protobuf.protobuf.Package;
@@ -81,9 +84,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
 
 /**
  * A scope provider for the Protobuf language.
@@ -436,62 +437,71 @@
    *
    * 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;
-    Resource descriptorResource = getDescriptorResource(literalLink);
-    EObject descriptor = descriptorResource.getContents().get(0);
-    scope = createLiteralLinkResolvedScope(reference, scope, descriptor);
-    scope = createLiteralLinkResolvedScope(reference, scope, literalLink);
-    return scope;
+  public @Nullable IScope scope_LiteralLink_target(LiteralLink literalLink, EReference reference) {
+    EObject container = literalLink.eContainer();
+    IndexedElement indexedElement = null;
+    if (container instanceof DefaultValueFieldOption) {
+      container = container.eContainer();
+      if (container instanceof IndexedElement) {
+        indexedElement = (IndexedElement) container;
+      }
+    }
+    if (container instanceof NativeFieldOption) {
+      indexedElement = ((NativeFieldOption) container).getSource().getTarget();
+    }
+    if (container instanceof NativeOption) {
+      indexedElement = ((NativeOption) container).getSource().getTarget();
+    }
+    if (container instanceof CustomFieldOption) {
+      EList<OptionField> fields = ((CustomFieldOption) container).getFields();
+      if (!fields.isEmpty()) {
+        indexedElement = fields.get(fields.size() - 1).getTarget();
+      } else {
+        indexedElement = ((CustomFieldOption) container).getSource().getTarget();
+      }
+    }
+    if (container instanceof CustomOption) {
+      EList<OptionField> fields = ((CustomOption) container).getFields();
+      if (!fields.isEmpty()) {
+        indexedElement = fields.get(fields.size() - 1).getTarget();
+      } else {
+        indexedElement = ((CustomOption) container).getSource().getTarget();
+      }
+    }
+    return createNormalizedScopeForIndexedElement(indexedElement, reference);
   }
 
-  private IScope createLiteralLinkResolvedScope(
-      EReference reference, IScope parent, EObject context) {
-    if (context == null) {
-      return parent;
-    }
-    final IScope parentScope =
-        createLiteralLinkResolvedScope(reference, parent, context.eContainer());
+  private IScope createNormalizedScopeForIndexedElement(
+      IndexedElement indexedElement, EReference reference) {
     return cache.get(
-        context,
-        context.eResource(),
+        indexedElement,
+        indexedElement.eResource(),
         new Provider<IScope>() {
           @Override
           public IScope get() {
-            List<ImportNormalizer> protoNormalizers = createEnumElementResolvers(context, false);
-            IScope scope = getSingleLevelProtobufImport(parentScope, context, reference);
-            if (scope instanceof ProtobufImportScope) {
-              for (ImportNormalizer normalizer : protoNormalizers) {
-                ((ProtobufImportScope) scope).addNormalizer(normalizer);
+            if (indexedElement instanceof MessageField) {
+              TypeLink typeLink = ((MessageField) indexedElement).getType();
+              if (typeLink instanceof ComplexTypeLink) {
+                ComplexType complexType = ((ComplexTypeLink) typeLink).getTarget();
+                if (complexType instanceof Enum) {
+                  IScope scope =
+                      getGlobalScopeProvider().getScope(complexType.eResource(), reference);
+                  ImportNormalizer normalizer = createImportNormalizer(complexType, false);
+                  scope =
+                      getLocalScopeProvider().getLocalElementsScope(scope, complexType, reference);
+                  ((ProtobufImportScope) scope).addNormalizer(normalizer);
+                  return scope;
+                }
               }
             }
-            return scope;
+            return null;
           }
         });
   }
 
-  /**
-   * Creates an {@code ImportNormalizer} for every {@code Enum} that is a descendant of {@code
-   * context}.
-   */
-  private List<ImportNormalizer> createEnumElementResolvers(EObject context, boolean ignoreCase) {
-    List<ImportNormalizer> importedNamespaceResolvers = new ArrayList<>();
-    for (EObject child : context.eContents()) {
-      if (child instanceof Enum) {
-        QualifiedName name = nameProvider.getFullyQualifiedName(child);
-        if (name != null && !name.isEmpty()) {
-          ImportNormalizer resolver =
-              getLocalScopeProvider().createImportedNamespaceResolver(name.toString(), ignoreCase);
-          if (resolver != null) {
-            importedNamespaceResolvers.add(resolver);
-          }
-        }
-      }
-      if (child instanceof Message) {
-        importedNamespaceResolvers.addAll(createEnumElementResolvers(child, ignoreCase));
-      }
-    }
-    return importedNamespaceResolvers;
+  private ImportNormalizer createImportNormalizer(EObject element, boolean ignoreCase) {
+    QualifiedName name = nameProvider.getFullyQualifiedName(element);
+    return getLocalScopeProvider().createImportedNamespaceResolver(name.toString(), ignoreCase);
   }
 
   /**
@@ -549,50 +559,39 @@
   public IScope scope_OptionSource_target(OptionSource optionSource, EReference reference) {
     String optionType = getOptionType(optionSource);
     Resource resource = optionSource.eResource();
-    IScope descriptorScope = cache.get(
-        optionType,
-        resource,
-        new Provider<IScope>() {
-          @Override
-          public IScope get() {
-            IScope scope = getGlobalScopeProvider().getScope(resource, reference);
-            Resource descriptorResource = getDescriptorResource(optionSource);
-            String descriptorMessage =
-                getPackageOfResource(descriptorResource)
-                    + nameConverter.getDelimiter()
-                    + optionType;
-            ImportNormalizer normalizer =
-                getLocalScopeProvider().createImportedNamespaceResolver(descriptorMessage, false);
-            scope =
-                getProtobufImportScope(
-                    scope, getDescriptorResource(optionSource).getContents().get(0), reference);
-            ((ProtobufImportScope) scope).addNormalizer(normalizer);
-            return scope;
-          }
-        });
-    return getProtobufImportScope(descriptorScope, optionSource, reference);
+    IScope descriptorScope =
+        cache.get(
+            optionType,
+            resource,
+            new Provider<IScope>() {
+              @Override
+              public IScope get() {
+                IScope scope = getGlobalScopeProvider().getScope(resource, reference);
+                Resource descriptorResource = getDescriptorResource(optionSource);
+                String descriptorMessage =
+                    getPackageOfResource(descriptorResource)
+                        + nameConverter.getDelimiter()
+                        + optionType;
+                ImportNormalizer normalizer =
+                    getLocalScopeProvider()
+                        .createImportedNamespaceResolver(descriptorMessage, false);
+                scope =
+                    createProtobufImportScope(
+                        scope, getDescriptorResource(optionSource).getContents().get(0), reference);
+                ((ProtobufImportScope) scope).addNormalizer(normalizer);
+                return scope;
+              }
+            });
+    return createProtobufImportScope(descriptorScope, optionSource, reference);
   }
 
-  /** Returns a multi level ProtobufImportScope {@code EObject}. */
-  private IScope getProtobufImportScope(
-      IScope parent, EObject context, EReference reference) {
+  private IScope createProtobufImportScope(IScope parent, EObject context, EReference reference) {
     IScope scope = parent;
     if (context.eContainer() == null) {
       scope = getLocalScopeProvider().getResourceScope(scope, context, reference);
     } else {
-      scope = getProtobufImportScope(scope, context.eContainer(), reference);
+      scope = createProtobufImportScope(scope, context.eContainer(), reference);
     }
     return getLocalScopeProvider().getLocalElementsScope(scope, context, reference);
   }
-
-  /** Returns a single level ProtobufImportScope {@code EObject}. */
-  private ProtobufImportScope getSingleLevelProtobufImport(
-      IScope parent, EObject context, EReference reference) {
-    IScope scope = parent;
-    if (context.eContainer() == null) {
-      scope = getLocalScopeProvider().getResourceScope(scope, context, reference);
-    }
-    return (ProtobufImportScope)
-        getLocalScopeProvider().getLocalElementsScope(scope, context, reference);
-  }
 }