In progress: [Issue 199] Add ability to navigate to proto element from
generated C++ code.

* Added support for enums.
* Made retrieval of alternative qualified names lazy.
* Code cleanup. 
* Added tests.
diff --git a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/actions/QualifiedNameFactory_createQualifiedNamesForComplexType_Test.java b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/actions/QualifiedNameFactory_createQualifiedNamesForComplexType_Test.java
deleted file mode 100644
index f21019f..0000000
--- a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/actions/QualifiedNameFactory_createQualifiedNamesForComplexType_Test.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2012 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.cdt.actions;
-
-import static com.google.eclipse.protobuf.cdt.junit.QualifiedNamesContain.containOnly;
-import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
-import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static org.junit.Assert.assertThat;
-
-import java.util.List;
-
-import org.eclipse.xtext.naming.QualifiedName;
-import org.junit.*;
-
-import com.google.eclipse.protobuf.cdt.ProtobufCdtModule;
-import com.google.eclipse.protobuf.junit.core.XtextRule;
-import com.google.inject.Inject;
-
-/**
- * Tests for <code>{@link QualifiedNameFactory#createQualifiedNamesForComplexType(String[])}</code>
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-public class QualifiedNameFactory_createQualifiedNamesForComplexType_Test {
-  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule(), new ProtobufCdtModule());
-
-  @Inject private QualifiedNameFactory qualifiedNameFactory;
-
-  @Test public void should_return_single_qualified_name_for_top_level_type() {
-    String[] segments = { "com", "google", "proto", "Test" };
-    List<QualifiedName> qualifiedNames = qualifiedNameFactory.createQualifiedNamesForComplexType(segments);
-    assertThat(qualifiedNames, containOnly("com.google.proto.Test"));
-  }
-
-  @Test public void should_split_in_underscore_for_one_level_nesting() {
-    String[] segments = { "com", "google", "proto", "Test_Inner" };
-    List<QualifiedName> qualifiedNames = qualifiedNameFactory.createQualifiedNamesForComplexType(segments);
-    assertThat(qualifiedNames, containOnly("com.google.proto.Test_Inner", "com.google.proto.Test.Inner"));
-  }
-
-  @Test public void should_split_in_underscore_for_multiple_level_nesting() {
-    String[] segments = { "com", "google", "proto", "Test_Inner_Inner" };
-    List<QualifiedName> qualifiedNames = qualifiedNameFactory.createQualifiedNamesForComplexType(segments);
-    assertThat(qualifiedNames, containOnly("com.google.proto.Test_Inner_Inner", "com.google.proto.Test.Inner.Inner"));
-  }}
diff --git a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/actions/ClassTypeQualifiedNameBuilder_createQualifiedNamesFrom_Test.java b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/fqn/ClassTypeQualifiedNameProviderStrategy_qualifiedNamesFrom_Test.java
similarity index 69%
rename from com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/actions/ClassTypeQualifiedNameBuilder_createQualifiedNamesFrom_Test.java
rename to com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/fqn/ClassTypeQualifiedNameProviderStrategy_qualifiedNamesFrom_Test.java
index 1fcc16d..469f454 100644
--- a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/actions/ClassTypeQualifiedNameBuilder_createQualifiedNamesFrom_Test.java
+++ b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/fqn/ClassTypeQualifiedNameProviderStrategy_qualifiedNamesFrom_Test.java
@@ -6,17 +6,15 @@
  *
  * http://www.eclipse.org/legal/epl-v10.html
  */
-package com.google.eclipse.protobuf.cdt.actions;
+package com.google.eclipse.protobuf.cdt.fqn;
 
+import static com.google.eclipse.protobuf.cdt.fqn.IsQualifiedNameSource.isQualifiedNameSourceWith;
 import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static java.util.Collections.singletonList;
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
-import java.util.List;
-
+import org.eclipse.cdt.core.dom.ast.IBinding;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
 import org.eclipse.cdt.internal.core.dom.parser.c.CASTName;
 import org.eclipse.cdt.internal.core.dom.parser.cpp.*;
@@ -28,25 +26,23 @@
 import com.google.inject.Inject;
 
 /**
- * Tests for <code>{@link ClassTypeQualifiedNameBuilder#createQualifiedNamesFrom(CPPClassType)}</code>
+ * Tests for <code>{@link ClassTypeQualifiedNameProviderStrategy#qualifiedNamesFrom(IBinding)}</code>
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 @SuppressWarnings("restriction")
-public class ClassTypeQualifiedNameBuilder_createQualifiedNamesFrom_Test {
+public class ClassTypeQualifiedNameProviderStrategy_qualifiedNamesFrom_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule(), new ProtobufCdtModule(), new TestModule());
 
   @Inject private CPPClassType classType;
-  @Inject private QualifiedNameFactory qualifiedNameFactory;
-  @Inject private ClassTypeQualifiedNameBuilder nameBuilder;
+  @Inject private ClassTypeQualifiedNameProviderStrategy nameBuilder;
 
-  @Test public void should_return_qualified_names_for_class_type() {
+  @Test public void should_return_qualified_names_for_class_type_if_it_extends_proto_file() {
     expectClassTypeToExtendProtoMessage();
     String[] segments = { "com", "google", "proto", "Test" };
     when(classType.getQualifiedName()).thenReturn(segments);
-    List<QualifiedName> expected = singletonList(QualifiedName.create(segments));
-    when(qualifiedNameFactory.createQualifiedNamesForComplexType(segments)).thenReturn(expected);
-    assertThat(nameBuilder.createQualifiedNamesFrom(classType), equalTo(expected));
+    Iterable<QualifiedName> qualifiedNames = nameBuilder.qualifiedNamesFrom(classType);
+    assertThat(qualifiedNames, isQualifiedNameSourceWith(segments));
   }
 
   private void expectClassTypeToExtendProtoMessage() {
@@ -64,10 +60,15 @@
     return qualifiedName;
   }
 
+  @Test public void should_return_null_if_class_type_does_not_extend_proto_message() {
+    when(classType.getBases()).thenReturn(new ICPPBase[0]);
+    Iterable<QualifiedName> qualifiedNames = nameBuilder.qualifiedNamesFrom(classType);
+    assertNull(qualifiedNames);
+  }
+
   private static class TestModule extends AbstractTestModule {
     @Override protected void configure() {
       mockAndBind(CPPClassType.class);
-      mockAndBind(QualifiedNameFactory.class);
     }
   }
 }
\ No newline at end of file
diff --git a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/fqn/IsQualifiedNameSource.java b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/fqn/IsQualifiedNameSource.java
new file mode 100644
index 0000000..a5417d1
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/fqn/IsQualifiedNameSource.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012 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.cdt.fqn;
+
+import org.eclipse.xtext.naming.QualifiedName;
+import org.hamcrest.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class IsQualifiedNameSource extends BaseMatcher<Iterable<QualifiedName>> {
+
+  private final QualifiedName original;
+
+  public static IsQualifiedNameSource isQualifiedNameSourceWith(String[] segments) {
+    return new IsQualifiedNameSource(segments);
+  }
+
+  private IsQualifiedNameSource(String[] segments) {
+    original = QualifiedName.create(segments);
+  }
+
+  @Override public boolean matches(Object item) {
+    if (!(item instanceof QualifiedNameSource)) {
+      return false;
+    }
+    QualifiedNameSource source = (QualifiedNameSource) item;
+    return original.equals(source.original());
+  }
+
+  @Override public void describeTo(Description description) {
+    description.appendValue(QualifiedNameSource.class.getSimpleName() + " with original qualified name: " + original);
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/junit/QualifiedNamesContain.java b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/junit/QualifiedNamesContain.java
deleted file mode 100644
index e2f213a..0000000
--- a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/junit/QualifiedNamesContain.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2012 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.cdt.junit;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-import java.util.List;
-
-import org.eclipse.xtext.naming.QualifiedName;
-import org.hamcrest.*;
-
-/**
- * @author alruiz@google.com (Alex Ruiz)
- */
-public class QualifiedNamesContain extends BaseMatcher<List<QualifiedName>> {
-  private final List<String> qualifiedNames;
-
-  public static QualifiedNamesContain containOnly(String...qualifiedNames) {
-    return new QualifiedNamesContain(qualifiedNames);
-  }
-
-  private QualifiedNamesContain(String[] qualifiedNames) {
-    this.qualifiedNames = newArrayList(qualifiedNames);
-  }
-
-  @SuppressWarnings("unchecked")
-  @Override public boolean matches(Object item) {
-    if (!(item instanceof List)) {
-      return false;
-    }
-    List<String> copy = newArrayList(qualifiedNames);
-    List<QualifiedName> actualNames = (List<QualifiedName>) item;
-    for (QualifiedName actual : actualNames) {
-      String expected = actual.toString();
-      copy.remove(expected);
-    }
-    return copy.isEmpty();
-  }
-
-  @Override public void describeTo(Description description) {
-    description.appendValue(qualifiedNames);
-  }
-}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/ProtobufCdtModule.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/ProtobufCdtModule.java
index 2774df5..5a4ee54 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/ProtobufCdtModule.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/ProtobufCdtModule.java
@@ -21,8 +21,8 @@
  */
 public class ProtobufCdtModule extends AbstractGenericModule {
   public void configureModelObjectDefinitionNavigator(Binder binder) {
-    bindToProtobufPluginObject(ModelObjectDefinitionNavigator.class, binder);
     bindToProtobufPluginObject(IPreferenceStoreAccess.class, binder);
+    bindToProtobufPluginObject(ModelObjectDefinitionNavigator.class, binder);
   }
 
   private <T> void bindToProtobufPluginObject(Class<T> type, Binder binder) {
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ModelObjectDefinitionQueryBuilder.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ModelObjectLookupQueryBuilder.java
similarity index 64%
rename from com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ModelObjectDefinitionQueryBuilder.java
rename to com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ModelObjectLookupQueryBuilder.java
index c5f9320..61e11a3 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ModelObjectDefinitionQueryBuilder.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ModelObjectLookupQueryBuilder.java
@@ -8,25 +8,26 @@
  */
 package com.google.eclipse.protobuf.cdt.actions;
 
-import static com.google.eclipse.protobuf.ui.editor.ModelObjectDefinitionNavigator.Query.query;
-import static java.lang.Math.max;
 import static org.eclipse.cdt.internal.ui.editor.ASTProvider.WAIT_NO;
 import static org.eclipse.core.runtime.Status.*;
 
-import java.util.Collection;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.cdt.core.dom.ast.*;
 import org.eclipse.cdt.core.model.*;
-import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClassType;
 import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable;
 import org.eclipse.cdt.internal.ui.editor.ASTProvider;
 import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.*;
 import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.*;
 import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.texteditor.ITextEditor;
 import org.eclipse.xtext.naming.QualifiedName;
 
+import com.google.eclipse.protobuf.cdt.fqn.QualifiedNameProvider;
+import com.google.eclipse.protobuf.cdt.path.ProtoFilePathFinder;
 import com.google.eclipse.protobuf.ui.editor.ModelObjectDefinitionNavigator.Query;
 import com.google.inject.Inject;
 
@@ -34,12 +35,17 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 @SuppressWarnings("restriction")
-class ModelObjectDefinitionQueryBuilder {
-  @Inject private ClassTypeQualifiedNameBuilder nameBuilder;
+class ModelObjectLookupQueryBuilder {
+  @Inject private QualifiedNameProvider qualifiedNameProvider;
   @Inject private ProtoFilePathFinder pathFinder;
 
-  Query buildQuery(final IEditorPart editor, final ITextSelection selection) {
-    final IPath protoFilePath = pathFinder.findProtoFilePath(editor);
+  Query buildQuery(IEditorPart editor) {
+    final int offset = selectionOffsetOf(editor);
+    if (offset < 0) {
+      return null;
+    }
+    IFile file = (IFile) editor.getEditorInput().getAdapter(IFile.class);
+    final IPath protoFilePath = pathFinder.findProtoFilePath(file);
     if (protoFilePath == null) {
       return null;
     }
@@ -54,21 +60,16 @@
         if (ast == null) {
           return CANCEL_STATUS;
         }
-        int offset = selection.getOffset();
-        int length = max(1, selection.getLength());
         IASTNodeSelector nodeSelector= ast.getNodeSelector(null);
-        IASTName selectedName = nodeSelector.findEnclosingName(offset, length);
+        IASTName selectedName = nodeSelector.findEnclosingName(offset, 1);
         if (selectedName == null) {
           return CANCEL_STATUS;
         }
         if (selectedName.isDefinition()) {
           IBinding binding = selectedName.resolveBinding();
-          if (binding instanceof CPPClassType) {
-            Collection<QualifiedName> qualifiedNames = nameBuilder.createQualifiedNamesFrom((CPPClassType) binding);
-            if (qualifiedNames.isEmpty()) {
-              return CANCEL_STATUS;
-            }
-            queriesReference.set(query(qualifiedNames, protoFilePath));
+          Iterable<QualifiedName> qualifiedNames = qualifiedNameProvider.qualifiedNamesFrom(binding);
+          if (qualifiedNames != null) {
+            queriesReference.set(Query.newQuery(qualifiedNames, protoFilePath));
             return OK_STATUS;
           }
         }
@@ -80,4 +81,14 @@
     }
     return queriesReference.get();
   }
+
+  private int selectionOffsetOf(IEditorPart editor) {
+    ISelectionProvider selectionProvider = ((ITextEditor) editor).getSelectionProvider();
+    ISelection selection = selectionProvider.getSelection();
+    if (selection instanceof ITextSelection) {
+      ITextSelection textSelection = (ITextSelection) selection;
+      return textSelection.getOffset();
+    }
+    return -1;
+  }
 }
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/OpenProtoDeclarationAction.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/OpenProtoDeclarationAction.java
index 25201bc..0c390f6 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/OpenProtoDeclarationAction.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/OpenProtoDeclarationAction.java
@@ -9,7 +9,6 @@
 package com.google.eclipse.protobuf.cdt.actions;
 
 import org.eclipse.jface.action.IAction;
-import org.eclipse.jface.text.ITextSelection;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.ui.*;
 
@@ -23,30 +22,23 @@
  */
 public class OpenProtoDeclarationAction implements IEditorActionDelegate {
   private IEditorPart editor;
-  private ITextSelection selection;
 
-  @Inject private ModelObjectDefinitionQueryBuilder queryBuilder;
+  @Inject private ModelObjectLookupQueryBuilder queryBuilder;
   @Inject private NavigationJobs navigationJobs;
 
   @Override public void run(IAction action) {
-    if (editor == null || selection == null) {
+    if (editor == null) {
       return;
     }
-    Query query = queryBuilder.buildQuery(editor, selection);
+    Query query = queryBuilder.buildQuery(editor);
     if (query != null) {
       navigationJobs.scheduleUsing(query);
     }
   }
 
-  @Override public void selectionChanged(IAction action, ISelection selection) {
-    if (selection instanceof ITextSelection) {
-      this.selection = (ITextSelection) selection;
-      return;
-    }
-    this.selection = null;
-  }
-
   @Override public void setActiveEditor(IAction action, IEditorPart editor) {
     this.editor = editor;
   }
+
+  @Override public void selectionChanged(IAction action, ISelection selection) {}
 }
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ProtoFilePathFinder.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ProtoFilePathFinder.java
deleted file mode 100644
index 9ee947e..0000000
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ProtoFilePathFinder.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2012 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.cdt.actions;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.eclipse.protobuf.ui.preferences.compiler.core.CompilerPreferences.compilerPreferences;
-import static org.eclipse.core.runtime.IPath.SEPARATOR;
-import static org.eclipse.xtext.util.Strings.*;
-
-import java.util.List;
-
-import org.eclipse.core.resources.*;
-import org.eclipse.core.runtime.*;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
-
-import com.google.eclipse.protobuf.ui.preferences.compiler.core.CompilerPreferences;
-import com.google.inject.Inject;
-
-/**
- * @author alruiz@google.com (Alex Ruiz)
- */
-class ProtoFilePathFinder {
-  private static final String PATH_SEPARATOR = new String(new char[] { SEPARATOR });
-
-  @Inject private IPreferenceStoreAccess storeAccess;
-
-  IPath findProtoFilePath(IEditorPart editor) {
-    IFile file = (IFile) editor.getEditorInput().getAdapter(IFile.class);
-    IPath headerFilePath = file.getFullPath();
-    if (!"h".equals(headerFilePath.getFileExtension())) {
-      return null;
-    }
-    String cppOutputDirectory = cppOutputDirectory(file.getProject());
-    if (isEmpty(cppOutputDirectory)) {
-      return null;
-    }
-    List<String> segments = newArrayList(headerFilePath.segments());
-    for (int i = 0; i < headerFilePath.segmentCount() - 1; i++) {
-      segments.remove(0);
-      if (headerFilePath.segment(i).equals(cppOutputDirectory)) {
-        break;
-      }
-    }
-    String headerFileName = headerFilePath.lastSegment();
-    segments.set(segments.size() - 1, headerFileName.replace("pb.h", "proto"));
-    String protoFilePath = concat(PATH_SEPARATOR, segments);
-    return new Path(protoFilePath);
-  }
-
-  private String cppOutputDirectory(IProject project) {
-    CompilerPreferences preferences = compilerPreferences(storeAccess, project);
-    if (!preferences.compileProtoFiles().getValue() && !preferences.cppCodeGenerationEnabled().getValue()) {
-      return null;
-    }
-    return preferences.cppOutputDirectory().getValue();
-  }
-}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/QualifiedNameFactory.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/QualifiedNameFactory.java
deleted file mode 100644
index 98389a8..0000000
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/QualifiedNameFactory.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2012 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.cdt.actions;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static java.util.Collections.unmodifiableList;
-
-import java.util.List;
-
-import org.eclipse.xtext.naming.QualifiedName;
-
-import com.google.eclipse.protobuf.model.util.QualifiedNames;
-import com.google.inject.*;
-
-/**
- * @author alruiz@google.com (Alex Ruiz)
- */
-@Singleton class QualifiedNameFactory {
-  @Inject private QualifiedNames qualifiedNames;
-
-  List<QualifiedName> createQualifiedNamesForComplexType(String[] segments) {
-    List<QualifiedName> names = newArrayList();
-    names.add(QualifiedName.create(segments));
-    int lastSegmentIndex = segments.length - 1;
-    String messageName = segments[lastSegmentIndex];
-    if (messageName.contains("_")) {
-      String[] hierarchicalNames = messageName.split("_");
-      List<String> newSegments = newArrayList(segments);
-      newSegments.remove(lastSegmentIndex);
-      newSegments.addAll(newArrayList(hierarchicalNames));
-      names.add(qualifiedNames.createFqn(newSegments));
-    }
-    return unmodifiableList(names);
-  }
-}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ClassTypeQualifiedNameBuilder.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/ClassTypeQualifiedNameProviderStrategy.java
similarity index 68%
rename from com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ClassTypeQualifiedNameBuilder.java
rename to com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/ClassTypeQualifiedNameProviderStrategy.java
index 907fa26..34fe592 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ClassTypeQualifiedNameBuilder.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/ClassTypeQualifiedNameProviderStrategy.java
@@ -6,32 +6,28 @@
  *
  * http://www.eclipse.org/legal/epl-v10.html
  */
-package com.google.eclipse.protobuf.cdt.actions;
-
-import static java.util.Collections.emptyList;
-
-import java.util.List;
+package com.google.eclipse.protobuf.cdt.fqn;
 
 import org.eclipse.cdt.core.dom.IName;
+import org.eclipse.cdt.core.dom.ast.IBinding;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
 import org.eclipse.cdt.internal.core.dom.parser.cpp.*;
 import org.eclipse.xtext.naming.QualifiedName;
 
-import com.google.inject.*;
+import com.google.inject.Singleton;
 
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
 @SuppressWarnings("restriction")
-@Singleton class ClassTypeQualifiedNameBuilder {
-  @Inject QualifiedNameFactory qualifiedNameFactory;
-
-  public List<QualifiedName> createQualifiedNamesFrom(CPPClassType classType) {
+@Singleton class ClassTypeQualifiedNameProviderStrategy implements QualifiedNameProviderStrategy<CPPClassType> {
+  @Override public Iterable<QualifiedName> qualifiedNamesFrom(IBinding binding) {
+    CPPClassType classType = supportedBindingType().cast(binding);
     if (isMessage(classType)) {
       String[] segments = classType.getQualifiedName();
-      return qualifiedNameFactory.createQualifiedNamesForComplexType(segments);
+      return new QualifiedNameSource(segments);
     }
-    return emptyList();
+    return null;
   }
 
   private boolean isMessage(CPPClassType classType) {
@@ -50,4 +46,8 @@
     String qualifiedNameAsText = qualifiedName.toString();
     return "::google::protobuf::Message".equals(qualifiedNameAsText);
   }
+
+  @Override public Class<CPPClassType> supportedBindingType() {
+    return CPPClassType.class;
+  }
 }
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/EnumQualifiedNameProviderStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/EnumQualifiedNameProviderStrategy.java
new file mode 100644
index 0000000..d904428
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/EnumQualifiedNameProviderStrategy.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012 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.cdt.fqn;
+
+import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPEnumeration;
+import org.eclipse.xtext.naming.QualifiedName;
+
+import com.google.inject.Singleton;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@SuppressWarnings("restriction")
+@Singleton class EnumQualifiedNameProviderStrategy implements QualifiedNameProviderStrategy<CPPEnumeration> {
+  @Override public Iterable<QualifiedName> qualifiedNamesFrom(IBinding binding) {
+    CPPEnumeration enumeration = supportedBindingType().cast(binding);
+    String[] segments = enumeration.getQualifiedName();
+    return new QualifiedNameSource(segments);
+  }
+
+  @Override public Class<CPPEnumeration> supportedBindingType() {
+    return CPPEnumeration.class;
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameProvider.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameProvider.java
new file mode 100644
index 0000000..03438b0
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012 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.cdt.fqn;
+
+import static com.google.common.collect.Maps.newHashMap;
+
+import java.util.Map;
+
+import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.xtext.naming.QualifiedName;
+
+import com.google.inject.Singleton;
+
+/**
+ * Provides all the possible qualified names of the protocol buffer element used to generate a C++ element.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@Singleton public class QualifiedNameProvider {
+  private final Map<Class<?>, QualifiedNameProviderStrategy<?>> strategies = newHashMap();
+
+  public QualifiedNameProvider() {
+    add(new ClassTypeQualifiedNameProviderStrategy());
+    add(new EnumQualifiedNameProviderStrategy());
+  }
+
+  private void add(QualifiedNameProviderStrategy<?> strategy) {
+    strategies.put(strategy.supportedBindingType(), strategy);
+  }
+
+  /**
+   * Returns a lazy-loaded <code>{@link Iterable}</code> containing all the possible qualified names of the protocol
+   * buffer element used to generate a C++ element.
+   * @param binding specifies the semantics of the name of the generated C++ element.
+   * @return a lazy-loaded {@code Iterable} containing all the possible qualified names, or {@code null} if qualified
+   * names cannot be obtained from the given {@code IBinding}.
+   */
+  public Iterable<QualifiedName> qualifiedNamesFrom(IBinding binding) {
+    QualifiedNameProviderStrategy<? extends IBinding> strategy = strategies.get(binding.getClass());
+    return (strategy != null) ? strategy.qualifiedNamesFrom(binding) : null;
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameProviderStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameProviderStrategy.java
new file mode 100644
index 0000000..44816d0
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameProviderStrategy.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012 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.cdt.fqn;
+
+import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.xtext.naming.QualifiedName;
+
+/**
+ * Provides all the possible qualified names of the protocol buffer element used to generate a C++ element described
+ * by a specific type of <code>{@link IBinding}</code>.
+ * @param <T> the type of {@code IBinding} this strategy supports.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+interface QualifiedNameProviderStrategy<T extends IBinding> {
+  Iterable<QualifiedName> qualifiedNamesFrom(IBinding binding);
+
+  Class<T> supportedBindingType();
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameSource.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameSource.java
new file mode 100644
index 0000000..062e1ac
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameSource.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012 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.cdt.fqn;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+import java.util.*;
+
+import org.eclipse.xtext.naming.QualifiedName;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.AbstractIterator;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class QualifiedNameSource implements Iterable<QualifiedName> {
+  private final QualifiedName original;
+
+  public QualifiedNameSource(String[] segments) {
+    original = QualifiedName.create(segments);
+  }
+
+  @VisibleForTesting QualifiedName original() {
+    return original;
+  }
+
+  @Override public Iterator<QualifiedName> iterator() {
+    return new QualifiedNameIterator();
+  }
+
+  private class QualifiedNameIterator extends AbstractIterator<QualifiedName> {
+    private int sentCount;
+
+    @Override protected QualifiedName computeNext() {
+      switch (sentCount++) {
+        case 0:
+          return original;
+        case 1:
+          return nested();
+        default:
+          return endOfData();
+      }
+    }
+
+    private QualifiedName nested() {
+      String name = original.getLastSegment();
+      if (!name.contains("_")) {
+        return endOfData();
+      }
+      String[] nestedNames = name.split("_");
+      List<String> newSegments = newArrayList(original.getSegments());
+      newSegments.remove(newSegments.size() - 1);
+      newSegments.addAll(newArrayList(nestedNames));
+      String[] segments = newSegments.toArray(new String[newSegments.size()]);
+      return QualifiedName.create(segments);
+    }
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/path/ProtoFilePathFinder.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/path/ProtoFilePathFinder.java
new file mode 100644
index 0000000..fd25b97
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/path/ProtoFilePathFinder.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2012 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.cdt.path;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.eclipse.protobuf.ui.preferences.compiler.core.CompilerPreferences.compilerPreferences;
+import static com.google.eclipse.protobuf.ui.util.Paths.segmentsOf;
+import static org.eclipse.core.runtime.IPath.SEPARATOR;
+import static org.eclipse.xtext.util.Strings.*;
+
+import java.util.List;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
+
+import com.google.eclipse.protobuf.ui.preferences.compiler.core.CompilerPreferences;
+import com.google.inject.Inject;
+
+/**
+ * Finds the path of a .proto file, given the path of the generated C++ header file.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ProtoFilePathFinder {
+  private static final String PATH_SEPARATOR = new String(new char[] { SEPARATOR });
+
+  @Inject private IPreferenceStoreAccess storeAccess;
+
+  /**
+   * Returns the path of the .proto file used as source of the generated C++ header file.
+   * @param file the given file.
+   * @return the path of the .proto file used as source of the generated C++ header file, or {@code null} if the given
+   * file is not a C++ header file or if C++ code generation is not enabled in the proto editor.
+   */
+  public IPath findProtoFilePath(IFile file) {
+    IPath headerFilePath = file.getFullPath();
+    if (!"h".equals(headerFilePath.getFileExtension())) {
+      return null;
+    }
+    IPath cppOutputDirectory = cppOutputDirectory(file.getProject());
+    if (cppOutputDirectory == null) {
+      return null;
+    }
+    List<String> newPathSegments = newArrayList(headerFilePath.segments());
+    for (int i = 0; i < headerFilePath.segmentCount() - 1; i++) {
+      newPathSegments.remove(0);
+      if (headerFilePath.segment(i).equals(cppOutputDirectory.lastSegment())) {
+        break;
+      }
+    }
+    int fileNameIndex = newPathSegments.size() - 1;
+    String fileName = newPathSegments.get(fileNameIndex);
+    newPathSegments.set(fileNameIndex, fileName.replace("pb.h", "proto"));
+    return new Path(concat(PATH_SEPARATOR, newPathSegments));
+  }
+
+  private IPath cppOutputDirectory(IProject project) {
+    CompilerPreferences preferences = compilerPreferences(storeAccess, project);
+    if (!preferences.compileProtoFiles().getValue() && !preferences.cppCodeGenerationEnabled().getValue()) {
+      return null;
+    }
+    String directoryName = preferences.cppOutputDirectory().getValue();
+    if (isEmpty(directoryName)) {
+      return null;
+    }
+    IFolder directory = null;
+    String[] segments = segmentsOf(directoryName);
+    StringBuilder pathBuilder = new StringBuilder();
+    for (String segment : segments) {
+      pathBuilder.append(segment);
+      directory = project.getFolder(pathBuilder.toString());
+      if (!directory.exists()) {
+        return null;
+      }
+      pathBuilder.append(SEPARATOR);
+    }
+    if (directory == null) {
+      return null;
+    }
+    return directory.getFullPath();
+  }
+}
diff --git a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/IndexLookup_resourceIn_Test.java b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/IndexLookup_resourceIn_Test.java
new file mode 100644
index 0000000..a6cb9b5
--- /dev/null
+++ b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/IndexLookup_resourceIn_Test.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2012 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.resource;
+
+import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
+import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.resource.*;
+import org.eclipse.xtext.resource.impl.ResourceSetBasedResourceDescriptions;
+import org.junit.*;
+
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.inject.Inject;
+
+/**
+ * Tests for <code>{@link IndexLookup#resourceIn(IPath)}</code>.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class IndexLookup_resourceIn_Test {
+  @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
+
+  @Inject private IndexLookup lookup;
+
+  // syntax = "proto2";
+  // package com.google.proto;
+  //
+  // message Person {}
+  @Test public void should_find_resource_if_URIs_are_exact_match() {
+    XtextResource resource = xtext.resource();
+    addToXtextIndex(resource);
+    URI resourceUri = resource.getURI();
+    IResourceDescription description = lookup.resourceIn(new Path(resourceUri.path()));
+    assertThat(description.getURI(), equalTo(resourceUri));
+  }
+
+  // syntax = "proto2";
+  // package com.google.proto;
+  //
+  // message Person {}
+  @Test public void should_find_resource_matching_segments_if_URIs_are_not_exact_match() {
+    XtextResource resource = xtext.resource();
+    addToXtextIndex(resource);
+    URI resourceUri = resource.getURI();
+    String[] segments = resourceUri.segments();
+    int segmentCount = segments.length;
+    String path = segments[segmentCount - 2] + "/" + segments[segmentCount - 1]; // last two segments.
+    IResourceDescription description = lookup.resourceIn(new Path(path));
+    assertThat(description.getURI(), equalTo(resourceUri));
+  }
+
+  // syntax = "proto2";
+  // package com.google.proto;
+  //
+  // message Person {}
+  @Test public void should_return_null_if_matching_URI_was_not_found() {
+    XtextResource resource = xtext.resource();
+    addToXtextIndex(resource);
+    IResourceDescription description = lookup.resourceIn(new Path("some/crazy/path"));
+    assertNull(description);
+  }
+
+  private void addToXtextIndex(XtextResource resource) {
+    IResourceDescriptions xtextIndex = lookup.getXtextIndex();
+    if (xtextIndex instanceof ResourceSetBasedResourceDescriptions) {
+      ((ResourceSetBasedResourceDescriptions) xtextIndex).setContext(resource);
+    }
+  }
+}
diff --git a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/ModelObjectLocationLookup_findModelObjectUri_Test.java b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/ResourceDescriptions_modelObjectUri_Test.java
similarity index 60%
rename from com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/ModelObjectLocationLookup_findModelObjectUri_Test.java
rename to com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/ResourceDescriptions_modelObjectUri_Test.java
index afa5d16..0d680f5 100644
--- a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/ModelObjectLocationLookup_findModelObjectUri_Test.java
+++ b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/ResourceDescriptions_modelObjectUri_Test.java
@@ -10,14 +10,12 @@
 
 import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static java.util.Collections.singletonList;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.*;
 
-import org.eclipse.core.runtime.*;
 import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.xtext.naming.*;
-import org.eclipse.xtext.naming.QualifiedName;
 import org.eclipse.xtext.resource.*;
 import org.eclipse.xtext.resource.impl.ResourceSetBasedResourceDescriptions;
 import org.junit.*;
@@ -27,15 +25,17 @@
 import com.google.inject.Inject;
 
 /**
- * Tests for <code>{@link ModelObjectLocationLookup#findModelObjectUri(Iterable, IPath)}</code>
+ * Tests for <code>{@link ResourceDescriptions#modelObjectUri(IResourceDescription, QualifiedName)}</code>
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
-public class ModelObjectLocationLookup_findModelObjectUri_Test {
+public class ResourceDescriptions_modelObjectUri_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
   @Inject private IQualifiedNameConverter fqnConverter;
-  @Inject private ModelObjectLocationLookup lookup;
+  @Inject private ResourceSetBasedResourceDescriptions index;
+  @Inject private ResourceDescriptions resourceDescriptions;
+
 
   // syntax = "proto2";
   // package com.google.proto;
@@ -46,34 +46,26 @@
   // }
   @Test public void should_find_URI_of_model_object_given_its_qualified_name() {
     XtextResource resource = xtext.resource();
-    addToXtextIndex(resource);
-    Iterable<QualifiedName> qualifiedNames = singletonList(fqnConverter.toQualifiedName("com.google.proto.Type"));
-    URI foundUri = lookup.findModelObjectUri(qualifiedNames, pathOf(resource));
+    QualifiedName qualifiedName = fqnConverter.toQualifiedName("com.google.proto.Type");
+    URI foundUri = resourceDescriptions.modelObjectUri(describe(resource), qualifiedName);
     Enum anEnum = xtext.find("Type", Enum.class);
     String fragment = resource.getURIFragment(anEnum);
     URI expectedUri = resource.getURI().appendFragment(fragment);
     assertThat(foundUri, equalTo(expectedUri));
   }
 
-  private IPath pathOf(XtextResource resource) {
-    return new Path(resource.getURI().path());
-  }
-
   // syntax = "proto2";
   // package com.google.proto;
   //
   // message Person {}
   @Test public void should_return_null_if_file_name_is_equal_but_file_path_is_not() {
-    addToXtextIndex(xtext.resource());
-    Iterable<QualifiedName> qualifiedNames = singletonList(fqnConverter.toQualifiedName("com.google.proto.Person"));
-    URI foundUri = lookup.findModelObjectUri(qualifiedNames, new Path("/test/src/protos/mytestmodel.proto"));
+    QualifiedName qualifiedName = fqnConverter.toQualifiedName("com.google.proto.Type");
+    URI foundUri = resourceDescriptions.modelObjectUri(describe(xtext.resource()), qualifiedName);
     assertNull(foundUri);
   }
 
-  private void addToXtextIndex(XtextResource resource) {
-    IResourceDescriptions xtextIndex = lookup.getXtextIndex();
-    if (xtextIndex instanceof ResourceSetBasedResourceDescriptions) {
-      ((ResourceSetBasedResourceDescriptions) xtextIndex).setContext(resource);
-    }
+  private IResourceDescription describe(Resource resource) {
+    index.setContext(resource);
+    return index.getResourceDescription(resource.getURI());
   }
 }
diff --git a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/editor/ModelObjectDefinitionNavigator_navigateToDefinition_Test.java b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/editor/ModelObjectDefinitionNavigator_navigateToDefinition_Test.java
index 6213098..75ce83f 100644
--- a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/editor/ModelObjectDefinitionNavigator_navigateToDefinition_Test.java
+++ b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/editor/ModelObjectDefinitionNavigator_navigateToDefinition_Test.java
@@ -10,7 +10,7 @@
 
 import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.ui.editor.ModelObjectDefinitionNavigator.Query.query;
+import static com.google.eclipse.protobuf.ui.editor.ModelObjectDefinitionNavigator.Query.newQuery;
 import static java.util.Collections.singletonList;
 import static org.eclipse.core.runtime.Status.*;
 import static org.eclipse.emf.common.util.URI.createURI;
@@ -22,11 +22,12 @@
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.xtext.naming.*;
 import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.resource.IResourceDescription;
 import org.eclipse.xtext.ui.editor.IURIEditorOpener;
 import org.junit.*;
 
 import com.google.eclipse.protobuf.junit.core.*;
-import com.google.eclipse.protobuf.resource.ModelObjectLocationLookup;
+import com.google.eclipse.protobuf.resource.*;
 import com.google.eclipse.protobuf.ui.editor.ModelObjectDefinitionNavigator.Query;
 import com.google.inject.Inject;
 
@@ -38,38 +39,48 @@
 public class ModelObjectDefinitionNavigator_navigateToDefinition_Test {
   private static IPath filePath;
 
-  @BeforeClass public static void setUp() {
+  @BeforeClass public static void setUpOnce() {
     filePath = new Path("/src/protos/test.proto");
   }
 
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule(), new TestModule());
 
-  @Inject private IQualifiedNameConverter fqnConverter;
-  @Inject private ModelObjectLocationLookup locationLookup;
   @Inject private IURIEditorOpener editorOpener;
+  @Inject private IQualifiedNameConverter fqnConverter;
+  @Inject private IndexLookup indexLookup;
+  @Inject private ResourceDescriptions resources;
   @Inject private ModelObjectDefinitionNavigator navigator;
 
+  private IResourceDescription resource;
+
+  @Before public void setUp() {
+    resource = mock(IResourceDescription.class);
+  }
+
   @Test public void should_navigate_to_model_object_if_URI_is_found() {
+    when(indexLookup.resourceIn(filePath)).thenReturn(resource);
+    QualifiedName qualifiedName = fqnConverter.toQualifiedName("com.google.proto.Type");
     URI uri = createURI("file:/usr/local/project/src/protos/test.proto");
-    Iterable<QualifiedName> qualifiedNames = singletonList(fqnConverter.toQualifiedName("com.google.proto.Type"));
-    when(locationLookup.findModelObjectUri(qualifiedNames, filePath)).thenReturn(uri);
-    IStatus result = navigator.navigateToDefinition(query(qualifiedNames, filePath));
+    when(resources.modelObjectUri(resource, qualifiedName)).thenReturn(uri);
+    IStatus result = navigator.navigateToDefinition(newQuery(singletonList(qualifiedName), filePath));
     assertThat(result, equalTo(OK_STATUS));
     verify(editorOpener).open(uri, true);
   }
 
   @Test public void should_not_navigate_to_model_object_if_URI_is_not_found() {
-    Iterable<QualifiedName> qualifiedNames = singletonList(fqnConverter.toQualifiedName("com.google.proto.Person"));
-    when(locationLookup.findModelObjectUri(qualifiedNames, filePath)).thenReturn(null);
-    IStatus result = navigator.navigateToDefinition(query(qualifiedNames, filePath));
+    when(indexLookup.resourceIn(filePath)).thenReturn(resource);
+    QualifiedName qualifiedName = fqnConverter.toQualifiedName("com.google.proto.Person");
+    when(resources.modelObjectUri(resource, qualifiedName)).thenReturn(null);
+    IStatus result = navigator.navigateToDefinition(newQuery(singletonList(qualifiedName), filePath));
     assertThat(result, equalTo(CANCEL_STATUS));
     verifyZeroInteractions(editorOpener);
   }
 
   private static class TestModule extends AbstractTestModule {
     @Override protected void configure() {
-      mockAndBind(ModelObjectLocationLookup.class);
       mockAndBind(IURIEditorOpener.class);
+      mockAndBind(IndexLookup.class);
+      mockAndBind(ResourceDescriptions.class);
     }
   }
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/protoc/OutputDirectory.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/protoc/OutputDirectory.java
index 67cbaf6..75cd929 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/protoc/OutputDirectory.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/protoc/OutputDirectory.java
@@ -9,7 +9,7 @@
 package com.google.eclipse.protobuf.ui.builder.protoc;
 
 import static com.google.eclipse.protobuf.ui.util.Paths.segmentsOf;
-import static java.io.File.separator;
+import static org.eclipse.core.runtime.IPath.SEPARATOR;
 
 import org.eclipse.core.resources.*;
 import org.eclipse.core.runtime.*;
@@ -41,7 +41,7 @@
         if (!directory.exists()) {
           directory.create(true, true, NO_MONITOR);
         }
-        path.append(separator);
+        path.append(SEPARATOR);
       }
     }
     return directory;
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ModelObjectDefinitionNavigator.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ModelObjectDefinitionNavigator.java
index f0393c7..3cec964 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ModelObjectDefinitionNavigator.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ModelObjectDefinitionNavigator.java
@@ -14,9 +14,10 @@
 import org.eclipse.core.runtime.*;
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.resource.IResourceDescription;
 import org.eclipse.xtext.ui.editor.IURIEditorOpener;
 
-import com.google.eclipse.protobuf.resource.ModelObjectLocationLookup;
+import com.google.eclipse.protobuf.resource.*;
 import com.google.inject.Inject;
 
 /**
@@ -25,8 +26,9 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class ModelObjectDefinitionNavigator {
-  @Inject private ModelObjectLocationLookup locationLookup;
+  @Inject private IndexLookup lookup;
   @Inject private IURIEditorOpener editorOpener;
+  @Inject private ResourceDescriptions resources;
 
   /**
    * Navigates to the definition of the model object whose qualified name matches any of the given ones. This method
@@ -35,10 +37,16 @@
    * @return the result of the operation.
     */
   public IStatus navigateToDefinition(Query query) {
-    URI uri = locationLookup.findModelObjectUri(query.qualifiedNames, query.filePath);
-    if (uri != null) {
-      editorOpener.open(uri, true);
-      return OK_STATUS;
+    IResourceDescription resource = lookup.resourceIn(query.filePath);
+    if (resource == null) {
+      return CANCEL_STATUS;
+    }
+    for (QualifiedName qualifiedName : query.qualifiedNames) {
+      URI uri = resources.modelObjectUri(resource, qualifiedName);
+      if (uri != null) {
+        editorOpener.open(uri, true);
+        return OK_STATUS;
+      }
     }
     return CANCEL_STATUS;
   }
@@ -59,7 +67,7 @@
      * @param filePath the path and name of the file where to perform the lookup.
      * @return the created {@code Query}.
      */
-    public static Query query(Iterable<QualifiedName> qualifiedNames, IPath filePath) {
+    public static Query newQuery(Iterable<QualifiedName> qualifiedNames, IPath filePath) {
       return new Query(qualifiedNames, filePath);
     }
 
@@ -67,5 +75,10 @@
       this.qualifiedNames = newLinkedList(qualifiedNames);
       this.filePath = filePath;
     }
+
+    @Override public String toString() {
+      String format = "%s[qualifiedNames=%s, filePath=%s]";
+      return String.format(format, getClass().getSimpleName(), qualifiedNames, filePath);
+    }
   }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Resources.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Resources.java
index 108152a..1627d2c 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Resources.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Resources.java
@@ -11,9 +11,6 @@
 import static org.eclipse.emf.common.util.URI.createURI;
 import static org.eclipse.emf.ecore.util.EcoreUtil.getAllContents;
 
-import com.google.eclipse.protobuf.protobuf.*;
-import com.google.inject.Inject;
-
 import org.eclipse.emf.common.util.*;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.resource.*;
@@ -21,6 +18,9 @@
 import org.eclipse.xtext.resource.XtextResource;
 import org.eclipse.xtext.scoping.impl.ImportUriResolver;
 
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.inject.Inject;
+
 /**
  * Utility methods related to <code>{@link Resource}</code>
  *
@@ -36,6 +36,7 @@
    * @return the resource referred by the URI of the given import, or {@code null} is the given {@code ResourceSet} does
    * not contain the resource.
    */
+  // TODO move to class ResourceSets
   public Resource importedResource(Import anImport, ResourceSet resourceSet) {
     try {
       URI importUri = createURI(uriResolver.apply(anImport));
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/resource/IndexLookup.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/resource/IndexLookup.java
new file mode 100644
index 0000000..65668fd
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/resource/IndexLookup.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2012 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.resource;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.resource.*;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.eclipse.protobuf.util.IPaths;
+import com.google.inject.Inject;
+
+/**
+ * Simplified Xtext index lookups.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class IndexLookup {
+  @Inject private IPaths paths;
+  @Inject private IResourceDescriptions xtextIndex;
+
+  /**
+   * Finds the resource description for the given path.
+   * @param path the given path.
+   * @return the found resource description, or {@code null} if a resource description with a matching path could not be
+   * found.
+   */
+  public IResourceDescription resourceIn(IPath path) {
+    IResourceDescription description = lookup(path);
+    if (description != null) {
+      return description;
+    }
+    return segmentMatching(path);
+  }
+
+  private IResourceDescription lookup(IPath path) {
+    URI uri = URI.createPlatformResourceURI(path.toOSString(), false);
+    return xtextIndex.getResourceDescription(uri);
+  }
+
+  private IResourceDescription segmentMatching(IPath path) {
+    for (IResourceDescription description : xtextIndex.getAllResourceDescriptions()) {
+      URI resourceUri = description.getURI();
+      if (paths.areReferringToSameFile(path, resourceUri)) {
+        return description;
+      }
+    }
+    return null;
+  }
+
+  @VisibleForTesting IResourceDescriptions getXtextIndex() {
+    return xtextIndex;
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/resource/ModelObjectLocationLookup.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/resource/ModelObjectLocationLookup.java
deleted file mode 100644
index 4c82a51..0000000
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/resource/ModelObjectLocationLookup.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2012 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.resource;
-
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.xtext.naming.QualifiedName;
-import org.eclipse.xtext.resource.*;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.eclipse.protobuf.util.IPaths;
-import com.google.inject.Inject;
-
-/**
- * Looks up the location of model objects in the Xtext index.
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-public class ModelObjectLocationLookup {
-  @Inject private IPaths paths;
-  @Inject private IResourceDescriptions xtextIndex;
-
-  /**
-   * Finds the URI of a model object whose qualified name matches any of the given ones.
-   * @param qualifiedNames all the possible qualified names the model object to look for may have.
-   * @param filePath the path and name of the file where to perform the lookup.
-   * @return the URI of a model object whose qualified name matches any of the given ones, or {@code null} if a
-   * matching model object cannot be found.
-   */
-  public URI findModelObjectUri(Iterable<QualifiedName> qualifiedNames, IPath filePath) {
-    for (IResourceDescription resourceDescription : xtextIndex.getAllResourceDescriptions()) {
-      URI resourceUri = resourceDescription.getURI();
-      if (paths.areReferringToSameFile(filePath, resourceUri)) {
-        // we found the resource we are looking for.
-        for (IEObjectDescription exported : resourceDescription.getExportedObjects()) {
-          QualifiedName modelObjectQualifiedName = exported.getQualifiedName();
-          for (QualifiedName qualifiedName : qualifiedNames) {
-            if (qualifiedName.equals(modelObjectQualifiedName)) {
-              return exported.getEObjectURI();
-            }
-          }
-        }
-        break;
-      }
-    }
-    return null;
-  }
-
-  @VisibleForTesting IResourceDescriptions getXtextIndex() {
-    return xtextIndex;
-  }
-}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/resource/ResourceDescriptions.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/resource/ResourceDescriptions.java
new file mode 100644
index 0000000..14a93f6
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/resource/ResourceDescriptions.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012 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.resource;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.resource.*;
+
+import com.google.inject.Singleton;
+
+/**
+ * Utility methods related to <code>{@link IResourceDescription}</code>s.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@Singleton public class ResourceDescriptions {
+  /**
+   * Finds the URI of a model object in the given resource whose qualified name matches the given one.
+   * @param resource the given resource.
+   * @param qualifiedName the qualified name to match.
+   * @return the URI of the found model object, or {@code null} if a model object with a matching URI could not be
+   * found.
+   */
+  public URI modelObjectUri(IResourceDescription resource, QualifiedName qualifiedName) {
+    for (IEObjectDescription exported : resource.getExportedObjects()) {
+      QualifiedName modelObjectQualifiedName = exported.getQualifiedName();
+      if (qualifiedName.equals(modelObjectQualifiedName)) {
+        return exported.getEObjectURI();
+      }
+    }
+    return null;
+  }
+}