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

* Made navigation from C++ class to message definition efficient.
diff --git a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy_createMappingFrom_Test.java b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy_createMappingFrom_Test.java
index c35c9a5..bbf3a63 100644
--- a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy_createMappingFrom_Test.java
+++ b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy_createMappingFrom_Test.java
@@ -8,21 +8,21 @@
  */
 package com.google.eclipse.protobuf.cdt.mapping;
 
-import static com.google.common.collect.ImmutableList.copyOf;
+import static com.google.common.collect.Lists.newArrayList;
 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.protobuf.ProtobufPackage.Literals.MESSAGE;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.when;
+
+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.*;
+import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClassType;
 import org.junit.*;
 
 import com.google.eclipse.protobuf.junit.core.*;
-import com.google.eclipse.protobuf.protobuf.Message;
 import com.google.inject.Inject;
 
 /**
@@ -34,41 +34,28 @@
 public class ClassMappingStrategy_createMappingFrom_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule(), new TestModule());
 
+  @Inject private IBindings bindings;
   @Inject private CPPClassType classType;
   @Inject private ClassMappingStrategy mappingStrategy;
 
-  @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);
+  @Test public void should_create_mapping_if_class_type_is_message() {
+    when(bindings.isMessage(classType)).thenReturn(true);
+    List<String> segments = newArrayList("com", "google", "proto", "Test");
+    when(bindings.qualifiedNameOf(classType)).thenReturn(segments);
     CppToProtobufMapping mapping = mappingStrategy.createMappingFrom(classType);
-    assertThat(mapping.qualifiedNameSegments(), equalTo(copyOf(segments)));
-    assertEquals(Message.class, mapping.type());
+    assertThat(mapping.qualifiedNameSegments(), equalTo(segments));
+    assertThat(mapping.type(), equalTo(MESSAGE));
   }
 
-  private void expectClassTypeToExtendProtoMessage() {
-    ICPPBase base = mock(ICPPBase.class);
-    when(classType.getBases()).thenReturn(new ICPPBase[] { base });
-    when(base.getBaseClassSpecifierName()).thenReturn(createQualifiedName("google", "protobuf", "Message"));
-  }
-
-  private CPPASTQualifiedName createQualifiedName(String...segments) {
-    CPPASTQualifiedName qualifiedName = new CPPASTQualifiedName();
-    for (String segment : segments) {
-      qualifiedName.addName(new CASTName(segment.toCharArray()));
-    }
-    qualifiedName.setFullyQualified(true);
-    return qualifiedName;
-  }
-
-  @Test public void should_return_null_if_class_type_does_not_extend_proto_message() {
-    when(classType.getBases()).thenReturn(new ICPPBase[0]);
+  @Test public void should_return_null_if_class_type_is_not_message() {
+    when(bindings.isMessage(classType)).thenReturn(false);
     CppToProtobufMapping mapping = mappingStrategy.createMappingFrom(classType);
     assertNull(mapping);
   }
 
   private static class TestModule extends AbstractTestModule {
     @Override protected void configure() {
+      mockAndBind(IBindings.class);
       mockAndBind(CPPClassType.class);
     }
   }
diff --git a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/MessageMatcherStrategy_matchingProtobufElementLocations_Test.java b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/MessageMatcherStrategy_matchingProtobufElementLocations_Test.java
new file mode 100644
index 0000000..da9064d
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/MessageMatcherStrategy_matchingProtobufElementLocations_Test.java
@@ -0,0 +1,93 @@
+/*
+ * 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 static com.google.common.collect.Lists.newArrayList;
+import static com.google.eclipse.protobuf.cdt.matching.URIsReferToEObjects.referTo;
+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.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.junit.*;
+
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.Message;
+import com.google.inject.Inject;
+
+/**
+ * Tests for <code>{@link MessageMatcherStrategy#matchingProtobufElementLocations(EObject, List)}</code>
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class MessageMatcherStrategy_matchingProtobufElementLocations_Test {
+  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
+
+  @Inject private MessageMatcherStrategy matcher;
+
+  // syntax = "proto2";
+  //
+  // message Person {}
+  //
+  // message Address {}
+  @Test public void should_find_top_level_perfect_match() {
+    List<URI> locations = matcher.matchingProtobufElementLocations(xtext.root(), newArrayList("Address"));
+    Message message = xtext.find("Address", Message.class);
+    assertThat(locations, referTo(message));
+  }
+
+  // syntax = "proto2";
+  //
+  // message Person {
+  //   message Address {}
+  // }
+  @Test public void should_find_nested_perfect_match() {
+    List<URI> locations = matcher.matchingProtobufElementLocations(xtext.root(), newArrayList("Person", "Address"));
+    Message message = xtext.find("Address", Message.class);
+    assertThat(locations, referTo(message));
+  }
+
+  // syntax = "proto2";
+  //
+  // message Person {
+  //   message Address {
+  //     message Type {}
+  //   }
+  // }
+  @Test public void should_find_nested_match_1() {
+    List<URI> locations = matcher.matchingProtobufElementLocations(xtext.root(), newArrayList("Person_Address_Type"));
+    Message message = xtext.find("Type", Message.class);
+    assertThat(locations, referTo(message));
+  }
+
+  // syntax = "proto2";
+  //
+  // message Person_Address {
+  //   message Type {}
+  // }
+  @Test public void should_find_nested_match_2() {
+    List<URI> locations = matcher.matchingProtobufElementLocations(xtext.root(), newArrayList("Person_Address_Type"));
+    Message message = xtext.find("Type", Message.class);
+    assertThat(locations, referTo(message));
+  }
+
+  // syntax = "proto2";
+  //
+  // message Person {
+  //   message Address_Type {}
+  // }
+  @Test public void should_find_nested_match_3() {
+    List<URI> locations = matcher.matchingProtobufElementLocations(xtext.root(), newArrayList("Person_Address_Type"));
+    Message message = xtext.find("Address_Type", Message.class);
+    assertThat(locations, referTo(message));
+  }
+}
diff --git a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/PatternBuilder_patternToMatchFrom_Test.java b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/PatternBuilder_patternToMatchFrom_Test.java
deleted file mode 100644
index f1ccfd0..0000000
--- a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/PatternBuilder_patternToMatchFrom_Test.java
+++ /dev/null
@@ -1,54 +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.matching;
-
-import static com.google.eclipse.protobuf.cdt.matching.PatternMatcher.matches;
-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.protobuf.ProtobufPackage.Literals.MESSAGE;
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertThat;
-
-import java.util.regex.Pattern;
-
-import org.junit.*;
-
-import com.google.eclipse.protobuf.cdt.mapping.CppToProtobufMapping;
-import com.google.eclipse.protobuf.junit.core.XtextRule;
-import com.google.inject.Inject;
-
-/**
- * Tests for <code>{@link PatternBuilder#patternToMatchFrom(CppToProtobufMapping)}</code>
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-public class PatternBuilder_patternToMatchFrom_Test {
-  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
-
-  @Inject private PatternBuilder builder;
-
-  @Test public void should_escape_dots() {
-    CppToProtobufMapping mapping = createMessageMapping("com", "google", "proto", "test", "Person");
-    Pattern pattern = builder.patternToMatchFrom(mapping);
-    assertThat(pattern.pattern(), equalTo("com\\.google\\.proto\\.test\\.Person"));
-    assertThat("com.google.proto.test.Person", matches(pattern));
-  }
-
-  @Test public void should_escape_underscore() {
-    CppToProtobufMapping mapping = createMessageMapping("com", "google", "proto", "test", "Person_PhoneType");
-    Pattern pattern = builder.patternToMatchFrom(mapping);
-    assertThat(pattern.pattern(), equalTo("com\\.google\\.proto\\.test\\.Person(\\.|_)PhoneType"));
-    assertThat("com.google.proto.test.Person.PhoneType", matches(pattern));
-    assertThat("com.google.proto.test.Person_PhoneType", matches(pattern));
-  }
-
-  private CppToProtobufMapping createMessageMapping(String... qualifiedNameSegments) {
-    return new CppToProtobufMapping(qualifiedNameSegments, MESSAGE);
-  }
-}
diff --git a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/PatternMatcher.java b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/PatternMatcher.java
deleted file mode 100644
index c7d4f07..0000000
--- a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/PatternMatcher.java
+++ /dev/null
@@ -1,38 +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.matching;
-
-import java.util.regex.Pattern;
-
-import org.hamcrest.*;
-
-/**
- * @author alruiz@google.com (Alex Ruiz)
- */
-class PatternMatcher extends TypeSafeMatcher<String> {
-  private final Pattern pattern;
-
-  static PatternMatcher matches(Pattern pattern) {
-    return new PatternMatcher(pattern);
-  }
-
-  private PatternMatcher(Pattern pattern) {
-    super(String.class);
-    this.pattern = pattern;
-  }
-
-  @Override public boolean matchesSafely(String item) {
-    return pattern.matcher(item).matches();
-  }
-
-  @Override public void describeTo(Description description) {
-    description.appendValue(pattern.pattern());
-  }
-
-}
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy.java
index 9e87acf..ecd7349 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/ClassMappingStrategy.java
@@ -20,12 +20,12 @@
  */
 @SuppressWarnings("restriction")
 class ClassMappingStrategy implements IBindingMappingStrategy<CPPClassType> {
-  @Inject private AstElements astElements;
+  @Inject private IBindings bindings;
 
   @Override public CppToProtobufMapping createMappingFrom(IBinding binding) {
     CPPClassType classType = typeOfSupportedBinding().cast(binding);
-    if (astElements.isMessage(classType)) {
-      return new CppToProtobufMapping(classType.getQualifiedName(), MESSAGE);
+    if (bindings.isMessage(classType)) {
+      return new CppToProtobufMapping(bindings.qualifiedNameOf(classType), MESSAGE);
     }
     return 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
index 9422974..deffd08 100644
--- 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
@@ -8,23 +8,21 @@
  */
 package com.google.eclipse.protobuf.cdt.mapping;
 
-import static com.google.common.collect.ImmutableList.copyOf;
+import java.util.List;
 
 import org.eclipse.emf.ecore.EClass;
 
-import com.google.common.collect.ImmutableList;
-
 /**
  * Information of the protocol buffer element obtained from a generated C++ element.
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class CppToProtobufMapping {
-  private final ImmutableList<String> qualifiedNameSegments;
+  private final List<String> qualifiedNameSegments;
   private final EClass type;
 
-  public CppToProtobufMapping(String[] qualifiedNameSegments, EClass type) {
-    this.qualifiedNameSegments = copyOf(qualifiedNameSegments);
+  CppToProtobufMapping(List<String> qualifiedNameSegments, EClass type) {
+    this.qualifiedNameSegments = qualifiedNameSegments;
     this.type = type;
   }
 
@@ -32,7 +30,7 @@
    * Returns the qualified name segments of the selected C++ element.
    * @return the qualified name segments of the selected C++ element.
    */
-  public ImmutableList<String> qualifiedNameSegments() {
+  public List<String> qualifiedNameSegments() {
     return qualifiedNameSegments;
   }
 
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
index c2db20c..aac7eb8 100644
--- 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
@@ -13,15 +13,18 @@
 import org.eclipse.cdt.core.dom.ast.IBinding;
 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPEnumeration;
 
+import com.google.inject.Inject;
+
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
 @SuppressWarnings("restriction")
 class EnumMappingStrategy implements IBindingMappingStrategy<CPPEnumeration> {
+  @Inject private IBindings bindings;
 
   @Override public CppToProtobufMapping createMappingFrom(IBinding binding) {
     CPPEnumeration enumeration = typeOfSupportedBinding().cast(binding);
-    return new CppToProtobufMapping(enumeration.getQualifiedName(), ENUM);
+    return new CppToProtobufMapping(bindings.qualifiedNameOf(enumeration), ENUM);
   }
 
   @Override public Class<CPPEnumeration> typeOfSupportedBinding() {
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/AstElements.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/IBindings.java
similarity index 64%
rename from com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/AstElements.java
rename to com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/IBindings.java
index 3dc7e4a..21e1b7a 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/AstElements.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/IBindings.java
@@ -9,10 +9,14 @@
 package com.google.eclipse.protobuf.cdt.mapping;
 
 import static com.google.common.collect.ImmutableList.of;
+import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Collections.unmodifiableList;
+
+import java.util.List;
 
 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.core.dom.ast.cpp.*;
 import org.eclipse.cdt.internal.core.dom.parser.cpp.*;
 
 import com.google.common.collect.ImmutableList;
@@ -21,7 +25,7 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 @SuppressWarnings("restriction")
-class AstElements {
+class IBindings {
   private static final ImmutableList<String> MESSAGE_SUPER_TYPES = of("::google::protobuf::Message",
       "google::protobuf::MessageLite");
 
@@ -45,4 +49,23 @@
     String qualifiedNameAsText = qualifiedName.toString();
     return MESSAGE_SUPER_TYPES.contains(qualifiedNameAsText);
   }
+
+  List<String> qualifiedNameOf(IBinding binding) {
+    List<String> segments = newArrayList();
+    for (IBinding owner = binding.getOwner(); owner != null; owner = owner.getOwner()) {
+      if (owner instanceof ICPPEnumeration && !((ICPPEnumeration) owner).isScoped()) {
+        continue;
+      }
+      String ownerName = owner.getName();
+      if (ownerName == null) {
+        break;
+      }
+      if (owner instanceof ICPPFunction || owner instanceof ICPPNamespace) {
+        continue;
+      }
+      segments.add(0, ownerName);
+    }
+    segments.add(binding.getName());
+    return unmodifiableList(segments);
+  }
 }
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/MethodMappingStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/MethodMappingStrategy.java
index dcef34a..fb3caba 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/MethodMappingStrategy.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/MethodMappingStrategy.java
@@ -14,11 +14,14 @@
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionType;
 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPMethod;
 
+import com.google.inject.Inject;
+
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
 @SuppressWarnings("restriction")
 class MethodMappingStrategy implements IBindingMappingStrategy<CPPMethod> {
+  @Inject private IBindings bindings;
 
   @Override public CppToProtobufMapping createMappingFrom(IBinding binding) {
     CPPMethod method = typeOfSupportedBinding().cast(binding);
@@ -30,7 +33,7 @@
     if (types != null && types.length > 0) {
       return null;
     }
-    return new CppToProtobufMapping(method.getQualifiedName(), MESSAGE_FIELD);
+    return new CppToProtobufMapping(bindings.qualifiedNameOf(method), MESSAGE_FIELD);
   }
 
   @Override public Class<CPPMethod> typeOfSupportedBinding() {
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/TypeDefMappingStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/TypeDefMappingStrategy.java
index 88fca85..0b5b4e7 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/TypeDefMappingStrategy.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/mapping/TypeDefMappingStrategy.java
@@ -20,12 +20,12 @@
  */
 @SuppressWarnings("restriction")
 class TypeDefMappingStrategy implements IBindingMappingStrategy<CPPTypedef> {
-  @Inject private AstElements astElements;
+  @Inject private IBindings bindings;
 
   @Override public CppToProtobufMapping createMappingFrom(IBinding binding) {
     CPPTypedef typeDef = typeOfSupportedBinding().cast(binding);
     IBinding owner = binding.getOwner();
-    if (!astElements.isMessage(owner)) {
+    if (!bindings.isMessage(owner)) {
       return null;
     }
     String typeName = typeNameOf(typeDef);
@@ -33,7 +33,7 @@
     if (typeName == null || !typeName.endsWith(typeNameSuffix)) {
       return null;
     }
-    return new CppToProtobufMapping(typeDef.getQualifiedName(), MESSAGE);
+    return new CppToProtobufMapping(bindings.qualifiedNameOf(typeDef), MESSAGE);
   }
 
   private String typeNameOf(CPPTypedef typeDef) {
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/MessageMatcherStrategy.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/MessageMatcherStrategy.java
index 1f9fe3c..0ccd200 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/MessageMatcherStrategy.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/MessageMatcherStrategy.java
@@ -8,8 +8,8 @@
  */
 package com.google.eclipse.protobuf.cdt.matching;
 
+import static com.google.common.base.Objects.equal;
 import static com.google.common.collect.Lists.newArrayList;
-import static com.google.eclipse.protobuf.cdt.matching.ContentsByType.contentsOf;
 import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.MESSAGE;
 import static java.util.Collections.unmodifiableList;
 
@@ -17,48 +17,73 @@
 
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.*;
-import org.eclipse.emf.ecore.resource.Resource;
 
+import com.google.eclipse.protobuf.model.util.ModelObjects;
 import com.google.eclipse.protobuf.protobuf.Message;
+import com.google.inject.Inject;
 
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
 class MessageMatcherStrategy implements ProtobufElementMatcherStrategy {
-  @Override public List<URI> matchingProtobufElementLocations(ContentsByType contents, String[] qualifiedNameSegments) {
+  @Inject private ModelObjects modelObjects;
+
+  @Override public List<URI> matchingProtobufElementLocations(EObject root, List<String> qualifiedName) {
     List<URI> matches = newArrayList();
-    int segmentCount = qualifiedNameSegments.length;
-    for (int i = 0; i < segmentCount; i++) {
-      String segment = qualifiedNameSegments[i];
-      for (EObject e : contents.ofType(supportedType())) {
-        if (!(e instanceof Message)) {
+    List<EObject> contents = root.eContents();
+    while (!qualifiedName.isEmpty()) {
+      String segment = qualifiedName.remove(0);
+      for (EObject o : contents) {
+        if (!isSupported(o)) {
           continue;
         }
-        Message message = (Message) e;
-        if (segment.equals(message.getName())) {
-          if (i == segmentCount - 1) {
-            // we found what we were looking for.
-            matches.add(uriOf(message));
-            break;
+        Message message = (Message) o;
+        if (equal(message.getName(), segment)) {
+          if (qualifiedName.isEmpty()) {
+            // this is the last segment. This message is a perfect match.
+            matches.add(modelObjects.uriOf(message));
+          } else {
+            // keep looking for match.
+            contents = message.eContents();
           }
-          // perfect segment match - go one level deeper.
-          contents = contentsOf(message);
-          continue;
+          break;
         }
         if (segment.contains("_")) {
-
+          matches.addAll(matchingNestedElementLocations(contents, segment));
+          break;
         }
       }
     }
     return unmodifiableList(matches);
   }
 
-  // TODO move to ModelObjects
-  private URI uriOf(EObject e) {
-    Resource resource = e.eResource();
-    URI uri = resource.getURI();
-    uri = uri.appendFragment(resource.getURIFragment(e));
-    return uri;
+  private List<URI> matchingNestedElementLocations(List<EObject> elements, String nestedQualifiedName) {
+    List<URI> matches = newArrayList();
+    for (EObject o : elements) {
+      if (!isSupported(o)) {
+        continue;
+      }
+      Message message = (Message) o;
+      String messageName = message.getName();
+      if (nestedQualifiedName.startsWith(messageName)) {
+        String rest = nestedQualifiedName.substring(messageName.length());
+        if (rest.isEmpty()) {
+          matches.add(modelObjects.uriOf(message));
+        }
+        else {
+          if (rest.startsWith("_")) {
+            rest = rest.substring(1);
+          }
+          matches.addAll(matchingNestedElementLocations(message.eContents(), rest));
+        }
+        break;
+      }
+    }
+    return matches;
+  }
+
+  private boolean isSupported(EObject o) {
+    return supportedType().equals(o.eClass());
   }
 
   @Override public EClass supportedType() {
diff --git a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatchFinder.java b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatchFinder.java
index cfd9596..fbebdee 100644
--- a/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatchFinder.java
+++ b/com.google.eclipse.protobuf.cdt/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatchFinder.java
@@ -8,23 +8,18 @@
  */
 package com.google.eclipse.protobuf.cdt.matching;
 
-import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Maps.newHashMap;
-import static com.google.eclipse.protobuf.cdt.matching.ContentsByType.contentsOf;
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.PACKAGE;
 import static java.util.Collections.emptyList;
 
 import java.util.*;
 
 import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.*;
+import org.eclipse.emf.ecore.EClass;
 import org.eclipse.emf.ecore.resource.Resource;
 
 import com.google.eclipse.protobuf.cdt.mapping.CppToProtobufMapping;
-import com.google.eclipse.protobuf.model.util.*;
-import com.google.eclipse.protobuf.protobuf.*;
-import com.google.eclipse.protobuf.protobuf.Package;
-import com.google.eclipse.protobuf.util.StringLists;
+import com.google.eclipse.protobuf.model.util.Resources;
+import com.google.eclipse.protobuf.protobuf.Protobuf;
 import com.google.inject.Inject;
 
 /**
@@ -33,9 +28,7 @@
 public class ProtobufElementMatchFinder {
   private final Map<EClass, ProtobufElementMatcherStrategy> strategies = newHashMap();
 
-  @Inject private Packages packages;
   @Inject private Resources resources;
-  @Inject private StringLists stringLists;
 
   @Inject public ProtobufElementMatchFinder(MessageMatcherStrategy s1) {
     register(s1);
@@ -48,35 +41,12 @@
   public List<URI> matchingProtobufElementLocations(Resource resource, CppToProtobufMapping mapping) {
     Protobuf root = resources.rootOf(resource);
     // TODO check for proto2?
-    ContentsByType contents = contentsOf(root);
-    String[] qualifiedNameSegments = removePackageFromQualifiedName(mapping.qualifiedNameSegments(), contents);
+    List<String> qualifiedNameSegments = mapping.qualifiedNameSegments();
     ProtobufElementMatcherStrategy strategy = strategies.get(mapping.type());
     if (strategy != null) {
-      return strategy.matchingProtobufElementLocations(contents, qualifiedNameSegments);
+      return strategy.matchingProtobufElementLocations(root, qualifiedNameSegments);
     }
     return emptyList();
   }
 
-  private String[] removePackageFromQualifiedName(List<String> qualifiedNameSegments, ContentsByType contents) {
-    Package aPackage = packageFrom(contents);
-    if (aPackage != null) {
-      return stringLists.toArray(qualifiedNameSegments);
-    }
-    List<String> segments = newArrayList(qualifiedNameSegments);
-    for (String packageSegment : packages.segmentsOf(aPackage)) {
-      if (segments.isEmpty() || !packageSegment.equals(segments.get(0))) {
-        break;
-      }
-      segments.remove(0);
-    }
-    return stringLists.toArray(segments);
-  }
-
-  private Package packageFrom(ContentsByType contents) {
-    List<EObject> packages = contents.ofType(PACKAGE);
-    if (packages.isEmpty()) {
-      return null;
-    }
-    return (Package) packages.get(0);
-  }
 }
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
index b4cb9e2..bc9704d 100644
--- 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
@@ -11,13 +11,13 @@
 import java.util.List;
 
 import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.*;
 
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
 interface ProtobufElementMatcherStrategy {
-  List<URI> matchingProtobufElementLocations(ContentsByType contents, String[] qualifiedNameSegments);
+  List<URI> matchingProtobufElementLocations(EObject root, List<String> qualifiedNameSegments);
 
   EClass supportedType();
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelObjects_uriOf_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelObjects_uriOf_Test.java
new file mode 100644
index 0000000..d607de5
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelObjects_uriOf_Test.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.model.util;
+
+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.assertThat;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.junit.*;
+
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.Message;
+import com.google.inject.Inject;
+
+/**
+ * Tests for <code>{@link ModelObjects#uriOf(EObject)}</code>
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ModelObjects_uriOf_Test {
+  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
+
+  @Inject private ModelObjects objects;
+
+  // syntax = "proto2";
+  //
+  // message Person {}
+  @Test public void should_return_uri_of_model_object() {
+    Message message = xtext.find("Person", Message.class);
+    URI expected = xtext.resource().getURI();
+    expected = expected.appendFragment(xtext.resource().getURIFragment(message));
+    assertThat(objects.uriOf(message), equalTo(expected));
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/ModelObjects.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/ModelObjects.java
index 6ec9da1..7aa3d9f 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/ModelObjects.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/ModelObjects.java
@@ -8,7 +8,9 @@
  */
 package com.google.eclipse.protobuf.model.util;
 
+import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.*;
+import org.eclipse.emf.ecore.resource.Resource;
 
 import com.google.eclipse.protobuf.protobuf.*;
 import com.google.eclipse.protobuf.protobuf.Package;
@@ -67,4 +69,15 @@
     }
     return (Protobuf) current;
   }
+
+  /**
+   * Returns the URI of the given model element.
+   * @param e the given model element.
+   * @return the URI of the given model element.
+   */
+  public URI uriOf(EObject e) {
+    Resource resource = e.eResource();
+    URI uri = resource.getURI();
+    return uri.appendFragment(resource.getURIFragment(e));
+  }
 }