[ Issue 194 ] Implement ability to navigate to model object given
its qualified name and file path.

Improved API.
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/ModelObjectLocationLookup_findModelObjectUri_Test.java
index 2511d12..51bdba7 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/ModelObjectLocationLookup_findModelObjectUri_Test.java
@@ -13,7 +13,10 @@
 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.naming.*;
+import org.eclipse.xtext.naming.QualifiedName;
 import org.eclipse.xtext.resource.*;
 import org.eclipse.xtext.resource.impl.ResourceSetBasedResourceDescriptions;
 import org.junit.*;
@@ -23,13 +26,14 @@
 import com.google.inject.Inject;
 
 /**
- * Tests for <code>{@link ModelObjectLocationLookup#findModelObjectUri(String, String)}</code>
+ * Tests for <code>{@link ModelObjectLocationLookup#findModelObjectUri(QualifiedName, IPath)}</code>
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class ModelObjectLocationLookup_findModelObjectUri_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
+  @Inject private IQualifiedNameConverter fqnConverter;
   @Inject private ModelObjectLocationLookup lookup;
 
   // syntax = "proto2";
@@ -42,20 +46,26 @@
   @Test public void should_find_URI_of_model_object_given_its_qualified_name() {
     XtextResource resource = xtext.resource();
     addToXtextIndex(resource);
-    URI foundUri = lookup.findModelObjectUri("com.google.proto.Type", resource.getURI().path());
+    QualifiedName qualifiedName = fqnConverter.toQualifiedName("com.google.proto.Type");
+    URI foundUri = lookup.findModelObjectUri(qualifiedName, pathOf(resource));
     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());
-    URI foundUri = lookup.findModelObjectUri("com.google.proto.Person", "/test/src/protos/mytestmodel.proto");
+    QualifiedName qualifiedName = fqnConverter.toQualifiedName("com.google.proto.Person");
+    URI foundUri = lookup.findModelObjectUri(qualifiedName, new Path("/test/src/protos/mytestmodel.proto"));
     assertNull(foundUri);
   }
 
diff --git a/com.google.eclipse.protobuf.ui.functional.test/src/com/google/eclipse/protobuf/ui/parser/PreferenceDrivenProtobufParser_doParse_Test.java b/com.google.eclipse.protobuf.ui.functional.test/src/com/google/eclipse/protobuf/ui/parser/PreferenceDrivenProtobufParser_doParse_Test.java
index b30f562..0c2ad39 100644
--- a/com.google.eclipse.protobuf.ui.functional.test/src/com/google/eclipse/protobuf/ui/parser/PreferenceDrivenProtobufParser_doParse_Test.java
+++ b/com.google.eclipse.protobuf.ui.functional.test/src/com/google/eclipse/protobuf/ui/parser/PreferenceDrivenProtobufParser_doParse_Test.java
@@ -22,7 +22,7 @@
 import com.google.eclipse.protobuf.junit.util.MultiLineTextBuilder;
 import com.google.eclipse.protobuf.parser.NonProto2Protobuf;
 import com.google.eclipse.protobuf.protobuf.Protobuf;
-import com.google.eclipse.protobuf.ui.ProtobufPlugIn;
+import com.google.eclipse.protobuf.ui.util.ProtobufPlugIn;
 import com.google.inject.Inject;
 
 /**
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 c4372a0..1c3b020 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,47 +10,58 @@
 
 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 org.eclipse.core.runtime.Status.*;
 import static org.eclipse.emf.common.util.URI.createURI;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.*;
 
-import com.google.eclipse.protobuf.junit.core.*;
-import com.google.eclipse.protobuf.resource.ModelObjectLocationLookup;
-import com.google.inject.Inject;
-
+import org.eclipse.core.runtime.*;
 import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.naming.*;
+import org.eclipse.xtext.naming.QualifiedName;
 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.ui.editor.ModelObjectDefinitionNavigator.Query;
+import com.google.inject.Inject;
+
 /**
- * Tests for <code>{@link ModelObjectDefinitionNavigator#navigateToDefinition(String, String)}</code>.
+ * Tests for <code>{@link ModelObjectDefinitionNavigator#navigateToDefinition(Query)}</code>.
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class ModelObjectDefinitionNavigator_navigateToDefinition_Test {
-  private static String filePath;
+  private static IPath filePath;
 
   @BeforeClass public static void setUp() {
-    filePath = "/src/protos/test.proto";
+    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 ModelObjectDefinitionNavigator navigator;
 
   @Test public void should_navigate_to_model_object_if_URI_is_found() {
     URI uri = createURI("file:/usr/local/project/src/protos/test.proto");
-    String qualifiedName = "com.google.proto.Type";
+    QualifiedName qualifiedName = fqnConverter.toQualifiedName("com.google.proto.Type");
     when(locationLookup.findModelObjectUri(qualifiedName, filePath)).thenReturn(uri);
-    navigator.navigateToDefinition(qualifiedName, filePath);
+    IStatus result = navigator.navigateToDefinition(query(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() {
-    String qualifiedName = "com.google.proto.Person";
+    QualifiedName qualifiedName = fqnConverter.toQualifiedName("com.google.proto.Person");
     when(locationLookup.findModelObjectUri(qualifiedName, filePath)).thenReturn(null);
-    navigator.navigateToDefinition(qualifiedName, filePath);
+    IStatus result = navigator.navigateToDefinition(query(qualifiedName, filePath));
+    assertThat(result, equalTo(CANCEL_STATUS));
     verifyZeroInteractions(editorOpener);
   }
 
diff --git a/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF b/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF
index 5c5cf31..95017c2 100644
--- a/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF
+++ b/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF
@@ -24,6 +24,8 @@
  org.apache.log4j,

  org.eclipse.ui.texteditor.spelling

 Bundle-RequiredExecutionEnvironment: JavaSE-1.6

-Export-Package: com.google.eclipse.protobuf.ui.contentassist.antlr,

- com.google.eclipse.protobuf.ui.contentassist

+Export-Package: com.google.eclipse.protobuf.ui.contentassist,

+ com.google.eclipse.protobuf.ui.contentassist.antlr,

+ com.google.eclipse.protobuf.ui.editor,

+ com.google.eclipse.protobuf.ui.util

 Bundle-Activator: com.google.eclipse.protobuf.ui.internal.ProtobufActivator

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 b90ad50..0a1c3c2 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
@@ -8,7 +8,11 @@
  */
 package com.google.eclipse.protobuf.ui.editor;
 
+import static org.eclipse.core.runtime.Status.*;
+
+import org.eclipse.core.runtime.*;
 import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.naming.QualifiedName;
 import org.eclipse.xtext.ui.editor.IURIEditorOpener;
 
 import com.google.eclipse.protobuf.resource.ModelObjectLocationLookup;
@@ -26,13 +30,41 @@
   /**
    * Navigates to the definition of the model object whose qualified name matches the given one. This method will open
    * the file containing the model object definition if necessary.
-   * @param qualifiedNameAsText the qualified name to match.
-   * @param filePath the path and name of the file where to perform the lookup. It should not include the host.
+   * @param query information needed to find the object model to navigate to.
+   * @return the result of the operation.
     */
-  public void navigateToDefinition(String qualifiedNameAsText, String filePath) {
-    URI uri = locationLookup.findModelObjectUri(qualifiedNameAsText, filePath);
+  public IStatus navigateToDefinition(Query query) {
+    URI uri = locationLookup.findModelObjectUri(query.qualifiedName, query.filePath);
     if (uri != null) {
       editorOpener.open(uri, true);
+      return OK_STATUS;
+    }
+    return CANCEL_STATUS;
+  }
+
+  /**
+   * Information needed to find the object model to navigate to.
+   *
+   * @author alruiz@google.com (Alex Ruiz)
+   */
+  public static class Query {
+    final QualifiedName qualifiedName;
+    final IPath filePath;
+
+    /**
+     * Creates a new <code>{@link Query}</code>, to be used by
+     * <code>{@link ModelObjectDefinitionNavigator#navigateToDefinition(Query)}</code>.
+     * @param qualifiedName the qualified name to match.
+     * @param filePath the path and name of the file where to perform the lookup.
+     * @return the created {@code Query}.
+     */
+    public static Query query(QualifiedName qualifiedName, IPath filePath) {
+      return new Query(qualifiedName, filePath);
+    }
+
+    private Query(QualifiedName qualifiedName, IPath filePath) {
+      this.qualifiedName = qualifiedName;
+      this.filePath = filePath;
     }
   }
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufPlugIn.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/ProtobufPlugIn.java
similarity index 94%
rename from com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufPlugIn.java
rename to com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/ProtobufPlugIn.java
index b8cf323..b4640ea 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufPlugIn.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/ProtobufPlugIn.java
@@ -6,7 +6,7 @@
  *
  * http://www.eclipse.org/legal/epl-v10.html
  */
-package com.google.eclipse.protobuf.ui;
+package com.google.eclipse.protobuf.ui.util;
 
 import com.google.eclipse.protobuf.ui.internal.ProtobufActivator;
 import com.google.inject.Injector;
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ProtobufValidation.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ProtobufValidation.java
index 179d527..64bfd73 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ProtobufValidation.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ProtobufValidation.java
@@ -23,7 +23,7 @@
 
 import com.google.eclipse.protobuf.model.util.Imports;
 import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.eclipse.protobuf.ui.ProtobufPlugIn;
+import com.google.eclipse.protobuf.ui.util.ProtobufPlugIn;
 
 /**
  * @author alruiz@google.com (Alex Ruiz)
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateFileOnActivation.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateFileOnActivation.java
index f4e3c6e..67fa6b3 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateFileOnActivation.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateFileOnActivation.java
@@ -16,9 +16,8 @@
 import org.eclipse.ui.*;
 import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
 
-import com.google.eclipse.protobuf.ui.ProtobufPlugIn;
 import com.google.eclipse.protobuf.ui.preferences.general.core.GeneralPreferences;
-import com.google.eclipse.protobuf.ui.util.Resources;
+import com.google.eclipse.protobuf.ui.util.*;
 
 /**
  * Validates a .proto file when it is opened or activated.
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
index 05d17df..83e093e 100644
--- 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
@@ -8,11 +8,13 @@
  */
 package com.google.eclipse.protobuf.resource;
 
+import org.eclipse.core.runtime.IPath;
 import org.eclipse.emf.common.util.URI;
-import org.eclipse.xtext.naming.*;
+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;
 
 /**
@@ -21,24 +23,24 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class ModelObjectLocationLookup {
+  @Inject private IPaths paths;
   @Inject private IResourceDescriptions xtextIndex;
-  @Inject private IQualifiedNameConverter fqnConverter;
 
   /**
    * Finds the URI of a model object whose qualified name matches the given one.
-   * @param qualifiedNameAsText the qualified name to match.
-   * @param filePath the path and name of the file where to perform the lookup. It should not include the host.
+   * @param qualifiedName the qualified name to match.
+   * @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 the given one, or {@code null} if a matching model
    * object cannot be found.
    */
-  public URI findModelObjectUri(String qualifiedNameAsText, String filePath) {
-    QualifiedName qualifiedName = fqnConverter.toQualifiedName(qualifiedNameAsText);
+  public URI findModelObjectUri(QualifiedName qualifiedName, IPath filePath) {
     for (IResourceDescription resourceDescription : xtextIndex.getAllResourceDescriptions()) {
       URI resourceUri = resourceDescription.getURI();
-      if (filePath.equals(resourceUri.path())) {
+      if (paths.areReferringToSameFile(filePath, resourceUri)) {
         // we found the resource we are looking for.
         for (IEObjectDescription exported : resourceDescription.getExportedObjects()) {
-          if (!exported.getEObjectOrProxy().eIsProxy() && qualifiedName.equals(exported.getQualifiedName())) {
+          QualifiedName modelObjectQualifiedName = exported.getQualifiedName();
+          if (qualifiedName.equals(modelObjectQualifiedName)) {
             return exported.getEObjectURI();
           }
         }
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
new file mode 100644
index 0000000..3cc88de
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/IPaths.java
@@ -0,0 +1,44 @@
+/*
+ * 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 com.google.common.base.Objects.equal;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.emf.common.util.URI;
+
+import com.google.inject.Singleton;
+
+/**
+ * Utility methods related to <code>{@link IPath}</code>s.
+ *
+ * @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.
+   * @param u the given URI.
+   * @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)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}