In progress: [ Issue 24 ]  Fix qualified names
https://code.google.com/p/protobuf-dt/issues/detail?id=24

Qualified names for types in the same message as the type reference are now successfully created.
diff --git a/com.google.eclipse.protobuf.junit/src/com/google/eclipse/protobuf/junit/util/Finder.java b/com.google.eclipse.protobuf.junit/src/com/google/eclipse/protobuf/junit/util/Finder.java
index 0d2decf..31583dc 100644
--- a/com.google.eclipse.protobuf.junit/src/com/google/eclipse/protobuf/junit/util/Finder.java
+++ b/com.google.eclipse.protobuf.junit/src/com/google/eclipse/protobuf/junit/util/Finder.java
@@ -13,12 +13,19 @@
 import java.util.List;
 
 import com.google.eclipse.protobuf.protobuf.*;
+import com.google.eclipse.protobuf.protobuf.Enum;
 
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
 public final class Finder {
 
+  public static Enum findEnum(String name, Protobuf root) {
+    for (Enum anEnum : getAllContentsOfType(root, Enum.class))
+      if (name.equals(anEnum.getName())) return anEnum;
+    return null;
+  }
+
   public static Message findMessage(String name, Protobuf root) {
     for (Message message : getAllContentsOfType(root, Message.class))
       if (name.equals(message.getName())) return message;
@@ -36,17 +43,17 @@
       if (name.equals(property.getName())) return property;
     return null;
   }
-  
+
   public static List<Property> allProperties(Protobuf root) {
     return getAllContentsOfType(root, Property.class);
   }
-  
+
   public static Literal findLiteral(String name, Protobuf root) {
     List<Literal> literals = getAllContentsOfType(root, Literal.class);
     for (Literal literal : literals)
       if (name.equals(literal.getName())) return literal;
         return null;
   }
-  
+
   private Finder() {}
 }
diff --git a/com.google.eclipse.protobuf.test/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.protobuf.test/.settings/org.eclipse.jdt.core.prefs
index f6d1a9a..d8e674c 100644
--- a/com.google.eclipse.protobuf.test/.settings/org.eclipse.jdt.core.prefs
+++ b/com.google.eclipse.protobuf.test/.settings/org.eclipse.jdt.core.prefs
@@ -1,8 +1,12 @@
-#Mon Apr 25 12:58:25 PDT 2011
+#Thu May 05 01:10:30 PDT 2011
 eclipse.preferences.version=1
 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
 org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 org.eclipse.jdt.core.compiler.source=1.5
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/AlternativeQualifiedNamesProvider_alternativeFullyQualifiedNames_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/AlternativeQualifiedNamesProvider_alternativeFullyQualifiedNames_Test.java
new file mode 100644
index 0000000..45f0b4d
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/AlternativeQualifiedNamesProvider_alternativeFullyQualifiedNames_Test.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 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.junit.util.Finder.findEnum;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.junit.*;
+
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.eclipse.protobuf.protobuf.Enum;
+
+/**
+ * Tests for <code>{@link AlternativeQualifiedNamesProvider#alternativeFullyQualifiedNames(EObject)}</code>.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class AlternativeQualifiedNamesProvider_alternativeFullyQualifiedNames_Test {
+
+  @Rule public XtextRule xtext = new XtextRule();
+
+  private AlternativeQualifiedNamesProvider namesProvider;
+
+  @Before public void setUp() {
+    namesProvider = xtext.getInstanceOf(AlternativeQualifiedNamesProvider.class);
+  }
+
+  @Test public void should_return_all_possible_qualified_names() {
+    StringBuilder proto = new StringBuilder();
+    proto.append("package alternative.names;                       ");
+    proto.append("                                                 ");
+    proto.append("message Person {                                 ");
+    proto.append("  message PhoneNumber {                          ");
+    proto.append("    optional PhoneType type = 1 [default = HOME];");
+    proto.append("                                                 ");
+    proto.append("    enum PhoneType {                             ");
+    proto.append("      HOME = 0;                                  ");
+    proto.append("      WORK = 1;                                  ");
+    proto.append("    }                                            ");
+    proto.append(" }                                               ");
+    proto.append("}                                                ");
+    Protobuf root = xtext.parse(proto);
+    Enum phoneType = findEnum("PhoneType", root);
+    List<QualifiedName> names = namesProvider.alternativeFullyQualifiedNames(phoneType);
+    assertThat(names.get(0).toString(), equalTo("PhoneType"));
+    assertThat(names.get(1).toString(), equalTo("PhoneNumber.PhoneType"));
+    assertThat(names.get(2).toString(), equalTo("Person.PhoneNumber.PhoneType"));
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/AlternativeQualifiedNamesProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/AlternativeQualifiedNamesProvider.java
new file mode 100644
index 0000000..fbb35d6
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/AlternativeQualifiedNamesProvider.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2011 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 org.eclipse.xtext.util.SimpleAttributeResolver.newResolver;
+import static org.eclipse.xtext.util.Strings.isEmpty;
+import static org.eclipse.xtext.util.Tuples.pair;
+
+import java.util.*;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.naming.*;
+import org.eclipse.xtext.util.*;
+
+import com.google.common.base.Function;
+import com.google.inject.*;
+
+/**
+ * Provides alternative qualified names for protobuf elements.
+ * <p>
+ * For example, given the following proto element:
+ * <pre>
+ * package alternative.names;
+ *
+ * message Person {
+ *   optional string name = 1;
+ *
+ *   enum PhoneType {
+ *     HOME = 0;
+ *     WORK = 1;
+ *   }
+ * }
+ * </pre>
+ * The default qualified name for {@code PhoneType} is {@code alternative.names.Person.PhoneType}. The problem is that
+ * protoc also recognizes the following as qualified names:
+ * <ul>
+ * <li>{@code PhoneType}</li>
+ * <li>{@code Person.PhoneType}</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This class provides the non-default qualified names recognized by protoc.
+ * </p>
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class AlternativeQualifiedNamesProvider {
+
+  // TODO: in example above, it looks like protoc considers names.Person.PhoneType as valid. In short, qualified
+  // names can use segments of the package as well.
+
+  @Inject private final IQualifiedNameConverter converter = new IQualifiedNameConverter.DefaultImpl();
+  @Inject private final IResourceScopeCache cache = IResourceScopeCache.NullImpl.INSTANCE;
+
+  private final Function<EObject, String> resolver = newResolver(String.class, "name");
+
+  List<QualifiedName> alternativeFullyQualifiedNames(final EObject obj) {
+    Pair<EObject, String> key = pair(obj, "fqns");
+    return cache.get(key, obj.eResource(), new Provider<List<QualifiedName>>() {
+      public List<QualifiedName> get() {
+        List<QualifiedName> names = new ArrayList<QualifiedName>();
+        EObject current = obj;
+        String name = resolver.apply(current);
+        if (isEmpty(name)) return names;
+        QualifiedName qualifiedName = converter.toQualifiedName(name);
+        names.add(qualifiedName);
+        while (current.eContainer() != null) {
+          current = current.eContainer();
+          String containerName = resolver.apply(current);
+          if (isEmpty(containerName)) continue;
+          qualifiedName = converter.toQualifiedName(containerName).append(qualifiedName);
+          names.add(qualifiedName);
+        }
+        return names;
+      }
+    });
+  }
+}
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 0275d65..e02d65d 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
@@ -10,26 +10,21 @@
 
 import static org.eclipse.emf.common.util.URI.createURI;
 import static org.eclipse.emf.ecore.util.EcoreUtil.getAllContents;
+import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
 import static org.eclipse.xtext.resource.EObjectDescription.create;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
 
-import org.eclipse.emf.common.util.TreeIterator;
-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.xtext.naming.IQualifiedNameProvider;
-import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.emf.common.util.*;
+import org.eclipse.emf.ecore.*;
+import org.eclipse.emf.ecore.resource.*;
+import org.eclipse.xtext.naming.*;
 import org.eclipse.xtext.resource.IEObjectDescription;
 import org.eclipse.xtext.scoping.IScope;
 import org.eclipse.xtext.scoping.impl.*;
 
 import com.google.eclipse.protobuf.protobuf.*;
 import com.google.eclipse.protobuf.protobuf.Enum;
-import com.google.eclipse.protobuf.protobuf.Package;
 import com.google.eclipse.protobuf.util.ProtobufElementFinder;
 import com.google.inject.Inject;
 
@@ -48,43 +43,29 @@
   @Inject private Globals globals;
   @Inject private IQualifiedNameProvider nameProvider;
   @Inject private ImportUriResolver uriResolver;
-  
+  @Inject private AlternativeQualifiedNamesProvider alternativeNamesProvider;
+
   @SuppressWarnings("unused")
   IScope scope_TypeReference_type(TypeReference typeRef, EReference reference) {
     Protobuf root = finder.rootOf(typeRef);
     List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
     Message message = (Message) typeRef.eContainer().eContainer();
-    addAllTypeDescriptionsInsideRoot(message, descriptions);
+    descriptions.addAll(allTypeDescriptionsInside(message));
     descriptions.addAll(importedTypes(root));
-    return createScope(descriptions);
+    IScope scope = createScope(descriptions);
+    System.out.println("scope for property: " + ((Property) typeRef.eContainer()).getName() + ": " + scope);
+    return scope;
   }
 
-  private void addAllTypeDescriptionsInsideRoot(Message root, List<IEObjectDescription> descriptions) {
-    TreeIterator<EObject> allContents = root.eAllContents();
-    while (allContents.hasNext()) {
-      EObject element = allContents.next();
-      if (!(element instanceof Type)) continue;
-      Type type = (Type) element;
-      descriptions.addAll(describeUsingQualifiedNames(type));
-      descriptions.add(create(type.getName(), type));
-    }
-  }
-  
-  private List<IEObjectDescription> describeUsingQualifiedNames(Type type) {
+  private List<IEObjectDescription> allTypeDescriptionsInside(Message root) {
     List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
-    QualifiedName fqn = nameProvider.getFullyQualifiedName(type);
-    descriptions.add(create(fqn, type));
-    QualifiedName fqnWithoutPackage = removePackage(fqn, type);
-    if (fqnWithoutPackage != null) descriptions.add(create(fqnWithoutPackage, type));
+    for (Type type : getAllContentsOfType(root, Type.class)) {
+      for (QualifiedName name : alternativeNamesProvider.alternativeFullyQualifiedNames(type))
+        descriptions.add(create(name, type));
+    }
     return descriptions;
   }
 
-  private QualifiedName removePackage(QualifiedName fqn, Type type) {
-    Package aPackage = finder.packageOf(type);
-    if (aPackage == null) return null;
-    return null;
-  }
-
   private List<IEObjectDescription> importedTypes(Protobuf root) {
     ResourceSet resourceSet = root.eResource().getResourceSet();
     List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
@@ -107,7 +88,7 @@
     }
     return descriptions;
   }
-  
+
   @SuppressWarnings("unused")
   IScope scope_LiteralRef_literal(LiteralRef literalRef, EReference reference) {
     EObject container = literalRef.eContainer();