Fixed: [Issue 194] Implement ability to navigate to model object given
its qualified name and file path.
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
new file mode 100644
index 0000000..2511d12
--- /dev/null
+++ b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/ModelObjectLocationLookup_findModelObjectUri_Test.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.resource;
+
+import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
+import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.*;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.resource.*;
+import org.eclipse.xtext.resource.impl.ResourceSetBasedResourceDescriptions;
+import org.junit.*;
+
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.Enum;
+import com.google.inject.Inject;
+
+/**
+ * Tests for <code>{@link ModelObjectLocationLookup#findModelObjectUri(String, String)}</code>
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ModelObjectLocationLookup_findModelObjectUri_Test {
+ @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
+
+ @Inject private ModelObjectLocationLookup lookup;
+
+ // syntax = "proto2";
+ // package com.google.proto;
+ //
+ // enum Type {
+ // ONE = 1;
+ // TWO = 2;
+ // }
+ @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());
+ Enum anEnum = xtext.find("Type", Enum.class);
+ String fragment = resource.getURIFragment(anEnum);
+ URI expectedUri = resource.getURI().appendFragment(fragment);
+ assertThat(foundUri, equalTo(expectedUri));
+ }
+
+ // 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");
+ assertNull(foundUri);
+ }
+
+ private void addToXtextIndex(XtextResource resource) {
+ IResourceDescriptions xtextIndex = lookup.getXtextIndex();
+ if (xtextIndex instanceof ResourceSetBasedResourceDescriptions) {
+ ((ResourceSetBasedResourceDescriptions) xtextIndex).setContext(resource);
+ }
+ }
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/AbstractTestModule.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/AbstractTestModule.java
new file mode 100644
index 0000000..6e24620
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/AbstractTestModule.java
@@ -0,0 +1,22 @@
+/*
+ * 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.junit.core;
+
+import static org.mockito.Mockito.mock;
+
+import com.google.inject.AbstractModule;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public abstract class AbstractTestModule extends AbstractModule {
+ protected <T> void createAndBindMock(Class<T> classToMock) {
+ binder().bind(classToMock).toInstance(mock(classToMock));
+ }
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
index dcb4de6..87216fd 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
@@ -10,7 +10,6 @@
import static com.google.eclipse.protobuf.junit.core.GeneratedProtoFiles.protoFile;
import static org.eclipse.xtext.util.Strings.isEmpty;
-import static org.mockito.Mockito.mock;
import java.io.File;
@@ -19,14 +18,13 @@
import com.google.eclipse.protobuf.protobuf.Import;
import com.google.eclipse.protobuf.scoping.IFileUriResolver;
-import com.google.inject.AbstractModule;
/**
* Guice module for unit testing.
*
* @author alruiz@google.com (Alex Ruiz)
*/
-public class IntegrationTestModule extends AbstractModule {
+public class IntegrationTestModule extends AbstractTestModule {
public static IntegrationTestModule integrationTestModule() {
return new IntegrationTestModule();
}
@@ -35,7 +33,7 @@
@Override protected void configure() {
binder().bind(IFileUriResolver.class).to(FileUriResolver.class);
- binder().bind(EReference.class).toInstance(mock(EReference.class));
+ createAndBindMock(EReference.class);
}
private static class FileUriResolver implements IFileUriResolver {
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/OverrideRuntimeModuleSetup.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/OverrideRuntimeModuleSetup.java
index 251358f..4b4c065 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/OverrideRuntimeModuleSetup.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/OverrideRuntimeModuleSetup.java
@@ -8,24 +8,27 @@
*/
package com.google.eclipse.protobuf.junit.core;
-import static org.eclipse.xtext.util.Modules2.mixin;
+import static java.util.Arrays.copyOf;
import com.google.eclipse.protobuf.*;
import com.google.inject.*;
-
+import com.google.inject.util.Modules;
/**
* @author alruiz@google.com (Alex Ruiz)
*/
public class OverrideRuntimeModuleSetup extends ProtobufStandaloneSetup {
- private final Module module;
+ private final Module[] modules;
- OverrideRuntimeModuleSetup(Module module) {
- this.module = module;
+ OverrideRuntimeModuleSetup(Module[] modules) {
+ this.modules = copyOf(modules, modules.length);
}
@Override public Injector createInjector() {
- Module mixin = mixin(new ProtobufRuntimeModule(), module);
- return Guice.createInjector(mixin);
+ Module current = new ProtobufRuntimeModule();
+ for (Module module : modules) {
+ current = Modules.override(current).with(module);
+ }
+ return Guice.createInjector(current);
}
}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/XtextRule.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/XtextRule.java
index 2eacb82..9679981 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/XtextRule.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/XtextRule.java
@@ -45,8 +45,8 @@
private XtextResource resource;
private Finder finder;
- public static XtextRule overrideRuntimeModuleWith(Module testModule) {
- return createWith(new OverrideRuntimeModuleSetup(testModule));
+ public static XtextRule overrideRuntimeModuleWith(Module...testModules) {
+ return createWith(new OverrideRuntimeModuleSetup(testModules));
}
public static XtextRule createWith(ISetup setup) {
@@ -79,7 +79,7 @@
public void parseText(String text) {
boolean ignoreSyntaxErrors = shouldIgnoreSyntaxErrorsIn(text);
- resource = resourceFrom(new StringInputStream(text));
+ resource = createResourceFrom(new StringInputStream(text));
IParseResult parseResult = resource.getParseResult();
root = (Protobuf) parseResult.getRootASTElement();
if (ignoreSyntaxErrors) {
@@ -103,15 +103,15 @@
return text.startsWith("// ignore errors");
}
- private XtextResource resourceFrom(InputStream input) {
- return resourceFrom(input, createURI("mytestmodel.proto"));
+ private XtextResource createResourceFrom(InputStream input) {
+ return createResourceFrom(input, createURI("file://localhost/project/src/protos/mytestmodel.proto"));
}
- private XtextResource resourceFrom(InputStream input, URI uri) {
- XtextResourceSet set = getInstanceOf(XtextResourceSet.class);
- set.setClasspathURIContext(getClass());
+ private XtextResource createResourceFrom(InputStream input, URI uri) {
+ XtextResourceSet resourceSet = getInstanceOf(XtextResourceSet.class);
+ resourceSet.setClasspathURIContext(getClass());
XtextResource resource = (XtextResource) getInstanceOf(IResourceFactory.class).createResource(uri);
- set.getResources().add(resource);
+ resourceSet.getResources().add(resource);
try {
resource.load(input, null);
} catch (IOException e) {
@@ -129,6 +129,10 @@
return injector.getInstance(type);
}
+ public XtextResource resource() {
+ return resource;
+ }
+
public Protobuf root() {
return root;
}
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
new file mode 100644
index 0000000..642f41b
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/editor/ModelObjectDefinitionNavigator_navigateToDefinition_Test.java
@@ -0,0 +1,59 @@
+/*
+ * 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.ui.editor;
+
+import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
+import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
+import static org.eclipse.emf.common.util.URI.createURI;
+import static org.mockito.Mockito.*;
+
+import org.eclipse.emf.common.util.URI;
+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.inject.Inject;
+
+/**
+ * Tests for <code>{@link ModelObjectDefinitionNavigator#navigateToDefinition(String, String)}</code>.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ModelObjectDefinitionNavigator_navigateToDefinition_Test {
+ @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule(), new TestModule());
+
+ @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://localhost/project/src/protos/test.proto");
+ String qualifiedName = "com.google.proto.Type";
+ String filePath = "/src/protos/test.proto";
+ when(locationLookup.findModelObjectUri(qualifiedName, filePath)).thenReturn(uri);
+ navigator.navigateToDefinition(qualifiedName, filePath);
+ 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";
+ String filePath = "/src/protos/test.proto";
+ when(locationLookup.findModelObjectUri(qualifiedName, filePath)).thenReturn(null);
+ navigator.navigateToDefinition(qualifiedName, filePath);
+ verifyZeroInteractions(editorOpener);
+ }
+
+ private static class TestModule extends AbstractTestModule {
+ @Override protected void configure() {
+ createAndBindMock(ModelObjectLocationLookup.class);
+ createAndBindMock(IURIEditorOpener.class);
+ }
+ }
+}
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
new file mode 100644
index 0000000..b90ad50
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ModelObjectDefinitionNavigator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.ui.editor;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.ui.editor.IURIEditorOpener;
+
+import com.google.eclipse.protobuf.resource.ModelObjectLocationLookup;
+import com.google.inject.Inject;
+
+/**
+ * Navigates to the definition of a model object, opening necessary files if necessary.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ModelObjectDefinitionNavigator {
+ @Inject private ModelObjectLocationLookup locationLookup;
+ @Inject private IURIEditorOpener editorOpener;
+
+ /**
+ * 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.
+ */
+ public void navigateToDefinition(String qualifiedNameAsText, String filePath) {
+ URI uri = locationLookup.findModelObjectUri(qualifiedNameAsText, filePath);
+ if (uri != null) {
+ editorOpener.open(uri, true);
+ }
+ }
+}
diff --git a/com.google.eclipse.protobuf/META-INF/MANIFEST.MF b/com.google.eclipse.protobuf/META-INF/MANIFEST.MF
index fa1ee42..00824d4 100644
--- a/com.google.eclipse.protobuf/META-INF/MANIFEST.MF
+++ b/com.google.eclipse.protobuf/META-INF/MANIFEST.MF
@@ -32,6 +32,7 @@
com.google.eclipse.protobuf.protobuf,
com.google.eclipse.protobuf.protobuf.impl,
com.google.eclipse.protobuf.protobuf.util,
+ com.google.eclipse.protobuf.resource,
com.google.eclipse.protobuf.scoping,
com.google.eclipse.protobuf.serializer,
com.google.eclipse.protobuf.services,
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
new file mode 100644
index 0000000..05d17df
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/resource/ModelObjectLocationLookup.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.resource;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.naming.*;
+import org.eclipse.xtext.resource.*;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.inject.Inject;
+
+/**
+ * Looks up the location of model objects in the Xtext index.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ModelObjectLocationLookup {
+ @Inject private 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.
+ * @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);
+ for (IResourceDescription resourceDescription : xtextIndex.getAllResourceDescriptions()) {
+ URI resourceUri = resourceDescription.getURI();
+ if (filePath.equals(resourceUri.path())) {
+ // we found the resource we are looking for.
+ for (IEObjectDescription exported : resourceDescription.getExportedObjects()) {
+ if (!exported.getEObjectOrProxy().eIsProxy() && qualifiedName.equals(exported.getQualifiedName())) {
+ return exported.getEObjectURI();
+ }
+ }
+ break;
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting IResourceDescriptions getXtextIndex() {
+ return xtextIndex;
+ }
+}