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

* Changed strategy to lookup protobuf elements.
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
deleted file mode 100644
index a5417d1..0000000
--- a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/fqn/IsQualifiedNameSource.java
+++ /dev/null
@@ -1,40 +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.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/fqn/ClassTypeQualifiedNameProviderStrategy_qualifiedNamesFrom_Test.java b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy_createMappingFrom_Test.java
similarity index 70%
rename from com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/fqn/ClassTypeQualifiedNameProviderStrategy_qualifiedNamesFrom_Test.java
rename to com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy_createMappingFrom_Test.java
index 469f454..756bad8 100644
--- a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/fqn/ClassTypeQualifiedNameProviderStrategy_qualifiedNamesFrom_Test.java
+++ b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy_createMappingFrom_Test.java
@@ -6,11 +6,11 @@
  *
  * http://www.eclipse.org/legal/epl-v10.html
  */
-package com.google.eclipse.protobuf.cdt.fqn;
+package com.google.eclipse.protobuf.cdt.mapping;
 
-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 org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
@@ -21,28 +21,29 @@
 import org.eclipse.xtext.naming.QualifiedName;
 import org.junit.*;
 
-import com.google.eclipse.protobuf.cdt.ProtobufCdtModule;
 import com.google.eclipse.protobuf.junit.core.*;
+import com.google.eclipse.protobuf.protobuf.Message;
 import com.google.inject.Inject;
 
 /**
- * Tests for <code>{@link ClassTypeQualifiedNameProviderStrategy#qualifiedNamesFrom(IBinding)}</code>
+ * Tests for <code>{@link ClassMappingStrategy#createMappingFrom(IBinding)}</code>
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 @SuppressWarnings("restriction")
-public class ClassTypeQualifiedNameProviderStrategy_qualifiedNamesFrom_Test {
-  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule(), new ProtobufCdtModule(), new TestModule());
+public class ClassMappingStrategy_createMappingFrom_Test {
+  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule(), new TestModule());
 
   @Inject private CPPClassType classType;
-  @Inject private ClassTypeQualifiedNameProviderStrategy nameBuilder;
+  @Inject private ClassMappingStrategy mappingStrategy;
 
-  @Test public void should_return_qualified_names_for_class_type_if_it_extends_proto_file() {
+  @Test public void should_return_qualified_name_for_class_type_if_it_extends_proto_file() {
     expectClassTypeToExtendProtoMessage();
     String[] segments = { "com", "google", "proto", "Test" };
     when(classType.getQualifiedName()).thenReturn(segments);
-    Iterable<QualifiedName> qualifiedNames = nameBuilder.qualifiedNamesFrom(classType);
-    assertThat(qualifiedNames, isQualifiedNameSourceWith(segments));
+    CppToProtobufMapping mapping = mappingStrategy.createMappingFrom(classType);
+    assertThat(mapping.qualifiedName(), equalTo(QualifiedName.create(segments)));
+    assertEquals(Message.class, mapping.type());
   }
 
   private void expectClassTypeToExtendProtoMessage() {
@@ -62,8 +63,8 @@
 
   @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);
+    CppToProtobufMapping mapping = mappingStrategy.createMappingFrom(classType);
+    assertNull(mapping);
   }
 
   private static class TestModule extends AbstractTestModule {
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 5a4ee54..ac97696 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
@@ -10,10 +10,13 @@
 
 import static com.google.eclipse.protobuf.cdt.ProtobufObjectsProvider.getfromProtobufPlugin;
 
+import org.eclipse.xtext.naming.IQualifiedNameConverter;
 import org.eclipse.xtext.service.AbstractGenericModule;
+import org.eclipse.xtext.ui.editor.IURIEditorOpener;
 import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
 
-import com.google.eclipse.protobuf.ui.editor.ModelObjectDefinitionNavigator;
+import com.google.eclipse.protobuf.model.util.*;
+import com.google.eclipse.protobuf.resource.*;
 import com.google.inject.Binder;
 
 /**
@@ -21,8 +24,13 @@
  */
 public class ProtobufCdtModule extends AbstractGenericModule {
   public void configureModelObjectDefinitionNavigator(Binder binder) {
+    bindToProtobufPluginObject(IndexLookup.class, binder);
     bindToProtobufPluginObject(IPreferenceStoreAccess.class, binder);
-    bindToProtobufPluginObject(ModelObjectDefinitionNavigator.class, binder);
+    bindToProtobufPluginObject(IQualifiedNameConverter.class, binder);
+    bindToProtobufPluginObject(IURIEditorOpener.class, binder);
+    bindToProtobufPluginObject(ModelObjects.class, binder);
+    bindToProtobufPluginObject(ResourceDescriptions.class, binder);
+    bindToProtobufPluginObject(Resources.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/AstBasedCppToProtobufMapper.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/AstBasedCppToProtobufMapper.java
new file mode 100644
index 0000000..231fda8
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/AstBasedCppToProtobufMapper.java
@@ -0,0 +1,71 @@
+/*
+ * 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 org.eclipse.cdt.internal.ui.editor.ASTProvider.WAIT_NO;
+import static org.eclipse.core.runtime.Status.*;
+
+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.model.ASTCache.ASTRunnable;
+import org.eclipse.cdt.internal.ui.editor.ASTProvider;
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.core.runtime.*;
+import org.eclipse.ui.IEditorPart;
+
+import com.google.eclipse.protobuf.cdt.editor.Editors;
+import com.google.eclipse.protobuf.cdt.mapping.*;
+import com.google.inject.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@SuppressWarnings("restriction")
+@Singleton class AstBasedCppToProtobufMapper {
+  @Inject private Editors editors;
+  @Inject private CppToProtobufMapper delegate;
+
+  CppToProtobufMapping createMappingFromSelectionOf(IEditorPart editor) {
+    final int offset = editors.selectionOffsetOf(editor);
+    if (offset < 0) {
+      return null;
+    }
+    IWorkingCopy workingCopy = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput());
+    if (workingCopy == null) {
+      return null;
+    }
+    final AtomicReference<CppToProtobufMapping> mappingReference = new AtomicReference<CppToProtobufMapping>();
+    ASTProvider astProvider = ASTProvider.getASTProvider();
+    IStatus status = astProvider.runOnAST(workingCopy, WAIT_NO, null, new ASTRunnable() {
+      @Override public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
+        if (ast == null) {
+          return CANCEL_STATUS;
+        }
+        IASTNodeSelector nodeSelector= ast.getNodeSelector(null);
+        IASTName selectedName = nodeSelector.findEnclosingName(offset, 1);
+        if (selectedName == null) {
+          return CANCEL_STATUS;
+        }
+        if (selectedName.isDefinition()) {
+          IBinding binding = selectedName.resolveBinding();
+          CppToProtobufMapping info = delegate.createMappingFrom(binding);
+          mappingReference.set(info);
+          return OK_STATUS;
+        }
+        return CANCEL_STATUS;
+      }
+    });
+    if (status == CANCEL_STATUS) {
+      return null;
+    }
+    return mappingReference.get();
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ModelObjectLookupQueryBuilder.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ModelObjectLookupQueryBuilder.java
deleted file mode 100644
index 61e11a3..0000000
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ModelObjectLookupQueryBuilder.java
+++ /dev/null
@@ -1,94 +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 org.eclipse.cdt.internal.ui.editor.ASTProvider.WAIT_NO;
-import static org.eclipse.core.runtime.Status.*;
-
-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.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;
-
-/**
- * @author alruiz@google.com (Alex Ruiz)
- */
-@SuppressWarnings("restriction")
-class ModelObjectLookupQueryBuilder {
-  @Inject private QualifiedNameProvider qualifiedNameProvider;
-  @Inject private ProtoFilePathFinder pathFinder;
-
-  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;
-    }
-    IWorkingCopy workingCopy = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput());
-    if (workingCopy == null) {
-      return null;
-    }
-    final AtomicReference<Query> queriesReference = new AtomicReference<Query>();
-    ASTProvider astProvider = ASTProvider.getASTProvider();
-    IStatus status = astProvider.runOnAST(workingCopy, WAIT_NO, null, new ASTRunnable() {
-      @Override public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
-        if (ast == null) {
-          return CANCEL_STATUS;
-        }
-        IASTNodeSelector nodeSelector= ast.getNodeSelector(null);
-        IASTName selectedName = nodeSelector.findEnclosingName(offset, 1);
-        if (selectedName == null) {
-          return CANCEL_STATUS;
-        }
-        if (selectedName.isDefinition()) {
-          IBinding binding = selectedName.resolveBinding();
-          Iterable<QualifiedName> qualifiedNames = qualifiedNameProvider.qualifiedNamesFrom(binding);
-          if (qualifiedNames != null) {
-            queriesReference.set(Query.newQuery(qualifiedNames, protoFilePath));
-            return OK_STATUS;
-          }
-        }
-        return CANCEL_STATUS;
-      }
-    });
-    if (status == CANCEL_STATUS) {
-      return null;
-    }
-    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/NavigationJobs.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/NavigationJobs.java
deleted file mode 100644
index 4b9ebe3..0000000
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/NavigationJobs.java
+++ /dev/null
@@ -1,35 +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 org.eclipse.core.runtime.Status.OK_STATUS;
-
-import org.eclipse.core.runtime.*;
-import org.eclipse.ui.progress.UIJob;
-
-import com.google.eclipse.protobuf.ui.editor.*;
-import com.google.eclipse.protobuf.ui.editor.ModelObjectDefinitionNavigator.Query;
-import com.google.inject.Inject;
-
-/**
- * @author alruiz@google.com (Alex Ruiz)
- */
-class NavigationJobs {
-  @Inject private ModelObjectDefinitionNavigator navigator;
-
-  void scheduleUsing(final Query query) {
-    UIJob job = new UIJob("Navigating to .proto file") {
-      @Override public IStatus runInUIThread(IProgressMonitor monitor) {
-        navigator.navigateToDefinition(query);
-        return OK_STATUS;
-      }
-    };
-    job.schedule();
-  }
-}
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 0c390f6..7b96f7e 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
@@ -8,11 +8,12 @@
  */
 package com.google.eclipse.protobuf.cdt.actions;
 
+import org.eclipse.emf.common.util.URI;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.ui.*;
+import org.eclipse.xtext.ui.editor.IURIEditorOpener;
 
-import com.google.eclipse.protobuf.ui.editor.ModelObjectDefinitionNavigator.Query;
 import com.google.inject.Inject;
 
 /**
@@ -21,18 +22,18 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class OpenProtoDeclarationAction implements IEditorActionDelegate {
-  private IEditorPart editor;
+  @Inject private IURIEditorOpener editorOpener;
+  @Inject private ProtobufElementUriFinder uriFinder;
 
-  @Inject private ModelObjectLookupQueryBuilder queryBuilder;
-  @Inject private NavigationJobs navigationJobs;
+  private IEditorPart editor;
 
   @Override public void run(IAction action) {
     if (editor == null) {
       return;
     }
-    Query query = queryBuilder.buildQuery(editor);
-    if (query != null) {
-      navigationJobs.scheduleUsing(query);
+    URI foundUri = uriFinder.findProtobufElementUriFromSelectionOf(editor);
+    if (foundUri != null) {
+      editorOpener.open(foundUri, true);
     }
   }
 
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ProtobufElementUriFinder.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ProtobufElementUriFinder.java
new file mode 100644
index 0000000..e496412
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/actions/ProtobufElementUriFinder.java
@@ -0,0 +1,62 @@
+/*
+ * 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 org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.resource.IResourceDescription;
+
+import com.google.eclipse.protobuf.cdt.editor.Editors;
+import com.google.eclipse.protobuf.cdt.mapping.CppToProtobufMapping;
+import com.google.eclipse.protobuf.cdt.matching.ProtobufElementMatcher;
+import com.google.eclipse.protobuf.cdt.path.ProtoFilePaths;
+import com.google.eclipse.protobuf.resource.*;
+import com.google.inject.Inject;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class ProtobufElementUriFinder {
+  @Inject private ProtobufElementMatcher matcher;
+  @Inject private ResourceDescriptions descriptions;
+  @Inject private Editors editors;
+  @Inject private IndexLookup indexLookup;
+  @Inject private AstBasedCppToProtobufMapper mapper;
+  @Inject private ProtoFilePaths paths;
+
+  URI findProtobufElementUriFromSelectionOf(IEditorPart editor) {
+    IFile file = editors.fileOpenIn(editor);
+    IPath protoFilePath = paths.protoFilePath(file);
+    if (protoFilePath != null) {
+      CppToProtobufMapping mapping = mapper.createMappingFromSelectionOf(editor);
+      if (mapping != null) {
+        IResourceDescription resource = indexLookup.resourceIn(protoFilePath);
+        return findProtobufElementUri(resource, mapping);
+      }
+    }
+    return null;
+  }
+
+  private URI findProtobufElementUri(IResourceDescription resource, CppToProtobufMapping mapping) {
+    if (resource == null) {
+      return null;
+    }
+    // try first direct lookup.
+    QualifiedName qualifiedName = mapping.qualifiedName();
+    URI foundUri = descriptions.modelObjectUri(resource, qualifiedName);
+    if (foundUri != null) {
+      return foundUri;
+    }
+    // try finding the best match.
+    return matcher.findUriOfMatchingProtobufElement(resource, mapping);
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/editor/Editors.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/editor/Editors.java
new file mode 100644
index 0000000..384317d
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/editor/Editors.java
@@ -0,0 +1,49 @@
+/*
+ * 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.editor;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import com.google.inject.Singleton;
+
+/**
+ * Utility methods related to editors.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@Singleton public class Editors {
+
+  /**
+   * Returns the offset of the selected text in the given editor.
+   * @param editor the given editor.
+   * @return  the offset of the selected text in the given editor, or -1 if there is no valid text information.
+   */
+  public 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;
+  }
+
+  /**
+   * Returns the file open in the given editor.
+   * @param editor the given editor.
+   * @return the file open in the given editor.
+   */
+  public IFile fileOpenIn(IEditorPart editor) {
+    return (IFile) editor.getEditorInput().getAdapter(IFile.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
deleted file mode 100644
index d904428..0000000
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/EnumQualifiedNameProviderStrategy.java
+++ /dev/null
@@ -1,31 +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.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
deleted file mode 100644
index 03438b0..0000000
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameProvider.java
+++ /dev/null
@@ -1,48 +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.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
deleted file mode 100644
index 44816d0..0000000
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameProviderStrategy.java
+++ /dev/null
@@ -1,25 +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.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
deleted file mode 100644
index 062e1ac..0000000
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/QualifiedNameSource.java
+++ /dev/null
@@ -1,65 +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.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/fqn/ClassTypeQualifiedNameProviderStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy.java
similarity index 71%
rename from com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/ClassTypeQualifiedNameProviderStrategy.java
rename to com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy.java
index 34fe592..00dacf4 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/fqn/ClassTypeQualifiedNameProviderStrategy.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy.java
@@ -6,7 +6,7 @@
  *
  * http://www.eclipse.org/legal/epl-v10.html
  */
-package com.google.eclipse.protobuf.cdt.fqn;
+package com.google.eclipse.protobuf.cdt.mapping;
 
 import org.eclipse.cdt.core.dom.IName;
 import org.eclipse.cdt.core.dom.ast.IBinding;
@@ -14,18 +14,21 @@
 import org.eclipse.cdt.internal.core.dom.parser.cpp.*;
 import org.eclipse.xtext.naming.QualifiedName;
 
+import com.google.eclipse.protobuf.protobuf.Message;
 import com.google.inject.Singleton;
 
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
 @SuppressWarnings("restriction")
-@Singleton class ClassTypeQualifiedNameProviderStrategy implements QualifiedNameProviderStrategy<CPPClassType> {
-  @Override public Iterable<QualifiedName> qualifiedNamesFrom(IBinding binding) {
-    CPPClassType classType = supportedBindingType().cast(binding);
+@Singleton class ClassMappingStrategy implements IBindingMappingStrategy<CPPClassType> {
+
+  @Override public CppToProtobufMapping createMappingFrom(IBinding binding) {
+    CPPClassType classType = typeOfSupportedBinding().cast(binding);
     if (isMessage(classType)) {
       String[] segments = classType.getQualifiedName();
-      return new QualifiedNameSource(segments);
+      QualifiedName qualifiedName = QualifiedName.create(segments);
+      return new CppToProtobufMapping(qualifiedName, Message.class);
     }
     return null;
   }
@@ -47,7 +50,7 @@
     return "::google::protobuf::Message".equals(qualifiedNameAsText);
   }
 
-  @Override public Class<CPPClassType> supportedBindingType() {
+  @Override public Class<CPPClassType> typeOfSupportedBinding() {
     return CPPClassType.class;
   }
 }
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/CppToProtobufMapper.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/CppToProtobufMapper.java
new file mode 100644
index 0000000..8f5f919
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/CppToProtobufMapper.java
@@ -0,0 +1,45 @@
+/*
+ * 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.mapping;
+
+import static com.google.common.collect.Maps.newHashMap;
+
+import java.util.Map;
+
+import org.eclipse.cdt.core.dom.ast.IBinding;
+
+import com.google.inject.Singleton;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@Singleton public class CppToProtobufMapper {
+  private final Map<Class<? extends IBinding>, IBindingMappingStrategy<?>> strategies = newHashMap();
+
+  public CppToProtobufMapper() {
+    register(new ClassMappingStrategy());
+    register(new EnumMappingStrategy());
+  }
+
+  private void register(IBindingMappingStrategy<?> strategy) {
+    strategies.put(strategy.typeOfSupportedBinding(), strategy);
+  }
+
+  /**
+   * Creates a <code>{@link CppToProtobufMapping}</code> from the C++ element whose name is described by the given
+   * {@code IBinding}.
+   * @param binding describes the name of a C++ element.
+   * @return a {@code ProtobufElementLookupInfo}, or {@code null} if the given binding does not correspond to a C++
+   * element that can be traced back to a protocol buffer element.
+   */
+  public CppToProtobufMapping createMappingFrom(IBinding binding) {
+    IBindingMappingStrategy<?> strategy = strategies.get(binding.getClass());
+    return (strategy != null) ? strategy.createMappingFrom(binding) : null;
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/CppToProtobufMapping.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/CppToProtobufMapping.java
new file mode 100644
index 0000000..695d380
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/CppToProtobufMapping.java
@@ -0,0 +1,43 @@
+/*
+ * 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.mapping;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.naming.QualifiedName;
+
+/**
+ * Information of the protocol buffer element obtained from a generated C++ element.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class CppToProtobufMapping {
+  private final QualifiedName qualifiedName;
+  private final Class<? extends EObject> type;
+
+  CppToProtobufMapping(QualifiedName qualifiedName, Class<? extends EObject> type) {
+    this.qualifiedName = qualifiedName;
+    this.type = type;
+  }
+
+  /**
+   * Returns the possible qualified name of the protocol buffer element.
+   * @return the possible qualified name of the protocol buffer element.
+   */
+  public QualifiedName qualifiedName() {
+    return qualifiedName;
+  }
+
+  /**
+   * Returns the type of the protocol buffer element.
+   * @return the type of the protocol buffer element.
+   */
+  public Class<? extends EObject> type() {
+    return type;
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/EnumMappingStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/EnumMappingStrategy.java
new file mode 100644
index 0000000..9c0edef
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/EnumMappingStrategy.java
@@ -0,0 +1,34 @@
+/*
+ * 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.mapping;
+
+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.eclipse.protobuf.protobuf.Enum;
+import com.google.inject.Singleton;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@SuppressWarnings("restriction")
+@Singleton class EnumMappingStrategy implements IBindingMappingStrategy<CPPEnumeration> {
+
+  @Override public CppToProtobufMapping createMappingFrom(IBinding binding) {
+    CPPEnumeration enumeration = typeOfSupportedBinding().cast(binding);
+    String[] segments = enumeration.getQualifiedName();
+    QualifiedName qualifiedName = QualifiedName.create(segments);
+    return new CppToProtobufMapping(qualifiedName, Enum.class);
+  }
+
+  @Override public Class<CPPEnumeration> typeOfSupportedBinding() {
+    return CPPEnumeration.class;
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/IBindingMappingStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/IBindingMappingStrategy.java
new file mode 100644
index 0000000..c57b3d7
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/IBindingMappingStrategy.java
@@ -0,0 +1,24 @@
+/*
+ * 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.mapping;
+
+import org.eclipse.cdt.core.dom.ast.IBinding;
+
+/**
+ * Derives a <code>{@link CppToProtobufMapping}</code> from the C++ element whose name is described by the given
+ * {@code IBinding}.
+ * @param <T> the type of {@code IBinding} supported.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+interface IBindingMappingStrategy <T extends IBinding> {
+  CppToProtobufMapping createMappingFrom(IBinding binding);
+
+  Class<T> typeOfSupportedBinding();
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ComplexTypeMatcherStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ComplexTypeMatcherStrategy.java
new file mode 100644
index 0000000..6302468
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ComplexTypeMatcherStrategy.java
@@ -0,0 +1,46 @@
+/*
+ * 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.matching;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.naming.IQualifiedNameConverter;
+import org.eclipse.xtext.resource.*;
+
+import com.google.eclipse.protobuf.cdt.mapping.CppToProtobufMapping;
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.eclipse.protobuf.protobuf.Enum;
+import com.google.eclipse.protobuf.resource.ResourceDescriptions;
+import com.google.inject.Inject;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class ComplexTypeMatcherStrategy implements ProtobufElementMatcherStrategy {
+  @Inject private IQualifiedNameConverter converter;
+  @Inject private ResourceDescriptions descriptions;
+
+  @Override public URI findUriOfMatchingProtobufElement(IResourceDescription resource, CppToProtobufMapping mapping) {
+    String qualifiedNameAsText = converter.toString(mapping.qualifiedName());
+    String regex = Pattern.quote(qualifiedNameAsText.replaceAll("_", "."));
+    Pattern pattern = Pattern.compile(regex);
+    List<IEObjectDescription> matches = descriptions.matchingQualifiedNames(resource, pattern);
+    if (matches.size() == 1) {
+      return matches.get(0).getEObjectURI();
+    }
+    return null;
+  }
+
+  @Override public boolean canHandle(Class<? extends EObject> protobufElementType) {
+    return protobufElementType.equals(Message.class) || protobufElementType.equals(Enum.class);
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatcher.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatcher.java
new file mode 100644
index 0000000..48e0e7c
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatcher.java
@@ -0,0 +1,36 @@
+/*
+ * 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.matching;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.resource.IResourceDescription;
+
+import com.google.eclipse.protobuf.cdt.mapping.CppToProtobufMapping;
+import com.google.inject.Inject;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ProtobufElementMatcher {
+  @Inject private ComplexTypeMatcherStrategy complexTypeMatcherStrategy;
+
+  /**
+   * Returns the URI of the protocol buffer element in the given resource, whose qualified name matches the one in the
+   * given <code>{@link CppToProtobufMapping}</code>.
+   * @param resource describes the contents of a .proto file.
+   * @param mapping information of the protocol buffer element to look for.
+   * @return the found URI, or {@code null} if it was not possible to find a matching protocol buffer element.
+   */
+  public URI findUriOfMatchingProtobufElement(IResourceDescription resource, CppToProtobufMapping mapping) {
+    if (complexTypeMatcherStrategy.canHandle(mapping.type())) {
+      return complexTypeMatcherStrategy.findUriOfMatchingProtobufElement(resource, mapping);
+    }
+    return null;
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatcherStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatcherStrategy.java
new file mode 100644
index 0000000..95326a8
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatcherStrategy.java
@@ -0,0 +1,24 @@
+/*
+ * 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.matching;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.resource.IResourceDescription;
+
+import com.google.eclipse.protobuf.cdt.mapping.CppToProtobufMapping;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+interface ProtobufElementMatcherStrategy {
+  URI findUriOfMatchingProtobufElement(IResourceDescription resource, CppToProtobufMapping mapping);
+
+  boolean canHandle(Class<? extends EObject> protobufElementType);
+}
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/ProtoFilePaths.java
similarity index 85%
rename from com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/path/ProtoFilePathFinder.java
rename to com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/path/ProtoFilePaths.java
index fd25b97..3f69641 100644
--- 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/ProtoFilePaths.java
@@ -24,30 +24,27 @@
 import com.google.inject.Inject;
 
 /**
- * Finds the path of a .proto file, given the path of the generated C++ header file.
+ * Utility methods related to paths of .proto files.
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
-public class ProtoFilePathFinder {
+public class ProtoFilePaths {
   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.
+   * @param cppHeaderFile the generated C++ header 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());
+  public IPath protoFilePath(IFile cppHeaderFile) {
+    IPath cppOutputDirectory = cppOutputDirectory(cppHeaderFile.getProject());
     if (cppOutputDirectory == null) {
       return null;
     }
+    IPath headerFilePath = cppHeaderFile.getFullPath();
     List<String> newPathSegments = newArrayList(headerFilePath.segments());
     for (int i = 0; i < headerFilePath.segmentCount() - 1; i++) {
       newPathSegments.remove(0);
@@ -57,7 +54,7 @@
     }
     int fileNameIndex = newPathSegments.size() - 1;
     String fileName = newPathSegments.get(fileNameIndex);
-    newPathSegments.set(fileNameIndex, fileName.replace("pb.h", "proto"));
+    newPathSegments.set(fileNameIndex, fileName.replace(".pb.h", ".proto"));
     return new Path(concat(PATH_SEPARATOR, newPathSegments));
   }
 
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/util/IPaths_areReferringToSameFile_Tests.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/util/IPaths_areReferringToSameFile_Tests.java
new file mode 100644
index 0000000..62a7d42
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/util/IPaths_areReferringToSameFile_Tests.java
@@ -0,0 +1,47 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.emf.common.util.URI;
+import org.junit.*;
+
+/**
+ * Tests for <code>{@link IPaths#areReferringToSameFile(IPath, URI)}</code>
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class IPaths_areReferringToSameFile_Tests {
+  private IPaths paths;
+
+  @Before public void setUp() {
+    paths = new IPaths();
+  }
+
+  @Test public void should_return_true_if_both_have_exactly_equal_segments() {
+    String pathValue = "/usr/local/google/proto";
+    IPath path = new Path(pathValue);
+    URI uri = URI.createPlatformResourceURI(pathValue, false);
+    assertTrue(paths.areReferringToSameFile(path, uri));
+  }
+
+  @Test public void should_return_true_if_path_is_subset_of_URI() {
+    IPath path = new Path("/google/proto");
+    URI uri = URI.createPlatformResourceURI("/usr/local/google/proto", false);
+    assertTrue(paths.areReferringToSameFile(path, uri));
+  }
+
+  @Test public void should_return_false_if_last_segments_in_path_and_URI_are_not_equal() {
+    IPath path = new Path("/usr/local/google/proto");
+    URI uri = URI.createPlatformResourceURI("/usr/local/google/cpp", false);
+    assertFalse(paths.areReferringToSameFile(path, uri));
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/naming/ProtobufQualifiedNameConverter.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/naming/ProtobufQualifiedNameConverter.java
index b0321cb..ecc2601 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/naming/ProtobufQualifiedNameConverter.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/naming/ProtobufQualifiedNameConverter.java
@@ -10,19 +10,20 @@
 
 import static org.eclipse.xtext.util.Strings.isEmpty;
 
-import com.google.eclipse.protobuf.util.Strings;
+import java.util.regex.Pattern;
 
 import org.eclipse.xtext.naming.IQualifiedNameConverter.DefaultImpl;
 import org.eclipse.xtext.naming.*;
 
-import java.util.regex.Pattern;
+import com.google.eclipse.protobuf.util.Strings;
+import com.google.inject.Singleton;
 
 /**
  * Provides support for multi-line qualified names.
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
-public class ProtobufQualifiedNameConverter extends DefaultImpl {
+@Singleton public class ProtobufQualifiedNameConverter extends DefaultImpl {
   private final Pattern delimiterPattern = Pattern.compile(delimiterPlusWhitespace());
 
   private String delimiterPlusWhitespace() {
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
index 14a93f6..b0416fb 100644
--- 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
@@ -8,11 +8,17 @@
  */
 package com.google.eclipse.protobuf.resource;
 
+import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Collections.unmodifiableList;
+
+import java.util.List;
+import java.util.regex.*;
+
 import org.eclipse.emf.common.util.URI;
-import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.naming.*;
 import org.eclipse.xtext.resource.*;
 
-import com.google.inject.Singleton;
+import com.google.inject.*;
 
 /**
  * Utility methods related to <code>{@link IResourceDescription}</code>s.
@@ -20,6 +26,8 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 @Singleton public class ResourceDescriptions {
+  @Inject private IQualifiedNameConverter converter;
+
   /**
    * Finds the URI of a model object in the given resource whose qualified name matches the given one.
    * @param resource the given resource.
@@ -36,4 +44,16 @@
     }
     return null;
   }
+
+  public List<IEObjectDescription> matchingQualifiedNames(IResourceDescription resource, Pattern pattern) {
+    List<IEObjectDescription> descriptions = newArrayList();
+    for (IEObjectDescription exported : resource.getExportedObjects()) {
+      QualifiedName qualifiedName = exported.getQualifiedName();
+      Matcher matcher = pattern.matcher(converter.toString(qualifiedName));
+      if (matcher.matches()) {
+        descriptions.add(exported);
+      }
+    }
+    return unmodifiableList(descriptions);
+  }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/IPaths.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/IPaths.java
index 3cc88de..1b9ac5c 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/IPaths.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/IPaths.java
@@ -21,7 +21,6 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 @Singleton public class IPaths {
-
   /**
    * Indicates whether the given path and URI refer to the same file.
    * @param p the given path.
@@ -29,13 +28,10 @@
    * @return {@code true} if the given path and URI refer to the same file, {@code false} otherwise.
    */
   public boolean areReferringToSameFile(IPath p, URI u) {
-    // TODO test
     int pIndex = p.segmentCount() - 1;
     int uIndex = u.segmentCount() - 1;
     while (pIndex >= 0 && uIndex >= 0) {
-      String pSegment = p.segment(pIndex--);
-      String uSegment = u.segment(uIndex--);
-      if (!equal(pSegment, uSegment)) {
+      if (!equal(p.segment(pIndex--), u.segment(uIndex--))) {
         return false;
       }
     }