In progress: [Issue 199] Add ability to navigate to proto element from
generated C++ code.
* Fixed pattern for qualified name matching
* Made matching more efficient
diff --git a/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatcher_findUriOfMatchingProtobufElement_Test.java b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatcher_findUriOfMatchingProtobufElement_Test.java
new file mode 100644
index 0000000..ac4e225
--- /dev/null
+++ b/com.google.eclipse.protobuf.cdt.test/src/com/google/eclipse/protobuf/cdt/matching/ProtobufElementMatcher_findUriOfMatchingProtobufElement_Test.java
@@ -0,0 +1,122 @@
+/*
+ * 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.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 org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.xtext.naming.*;
+import org.eclipse.xtext.resource.*;
+import org.eclipse.xtext.resource.impl.ResourceSetBasedResourceDescriptions;
+import org.junit.*;
+
+import com.google.eclipse.protobuf.cdt.mapping.CppToProtobufMapping;
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.Message;
+import com.google.inject.Inject;
+
+/**
+ * Tests for <code>{@link ProtobufElementMatcher#findUriOfMatchingProtobufElement(IResourceDescription, CppToProtobufMapping)}</code>
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ProtobufElementMatcher_findUriOfMatchingProtobufElement_Test {
+ @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
+
+ @Inject private IQualifiedNameConverter fqnConverter;
+ @Inject private ResourceSetBasedResourceDescriptions index;
+ @Inject private ProtobufElementMatcher matcher;
+
+ // syntax = "proto2";
+ // package com.google.proto;
+ //
+ // message Outer {
+ // message Inner {}
+ // }
+ @Test public void should_return_URI_nested_message_when_qualified_name_does_not_contain_underscores() {
+ CppToProtobufMapping mapping = messageMapping("com.google.proto.Outer.Inner");
+ URI foundUri = matcher.findUriOfMatchingProtobufElement(descriptionOf(xtext.resource()), mapping);
+ assertThat(foundUri, equalTo(uriOfMessageWithName("Inner")));
+ }
+
+ // syntax = "proto2";
+ // package com.google.proto;
+ //
+ // message Outer {
+ // message Inner {}
+ // }
+ @Test public void should_return_URI_nested_message_when_underscores_in_qualified_name_represent_nesting() {
+ CppToProtobufMapping mapping = messageMapping("com.google.proto.Outer_Inner");
+ URI foundUri = matcher.findUriOfMatchingProtobufElement(descriptionOf(xtext.resource()), mapping);
+ assertThat(foundUri, equalTo(uriOfMessageWithName("Inner")));
+ }
+
+ // syntax = "proto2";
+ // package com.google.proto;
+ //
+ // message Outer_Message {
+ // message Inner {}
+ // }
+ @Test public void should_return_URI_nested_message_when_underscores_are_part_of_naming() {
+ CppToProtobufMapping mapping = messageMapping("com.google.proto.Outer_Message_Inner");
+ URI foundUri = matcher.findUriOfMatchingProtobufElement(descriptionOf(xtext.resource()), mapping);
+ assertThat(foundUri, equalTo(uriOfMessageWithName("Inner")));
+ }
+
+ // syntax = "proto2";
+ // package com.google.proto;
+ //
+ // message Outer_Message {
+ // message Inner {}
+ // }
+ //
+ // message Outer {
+ // message Message {
+ // message Inner {}
+ // }
+ // }
+ @Test public void should_return_first_match_if_both_qualified_names_and_types_match() {
+ CppToProtobufMapping mapping = messageMapping("com.google.proto.Outer_Message_Inner");
+ URI foundUri = matcher.findUriOfMatchingProtobufElement(descriptionOf(xtext.resource()), mapping);
+ assertThat(foundUri, equalTo(uriOfMessageWithName("Inner")));
+ }
+
+ // syntax = "proto2";
+ // package com.google.proto;
+ //
+ // message Outer {
+ // message Inner {}
+ // }
+ @Test public void should_return_null_if_match_not_found() {
+ CppToProtobufMapping mapping = messageMapping("com.google.proto.Outer__Inner");
+ URI foundUri = matcher.findUriOfMatchingProtobufElement(descriptionOf(xtext.resource()), mapping);
+ assertNull(foundUri);
+ }
+
+ private CppToProtobufMapping messageMapping(String qualifiedNameAsText) {
+ QualifiedName qualifiedName = fqnConverter.toQualifiedName(qualifiedNameAsText);
+ return new CppToProtobufMapping(qualifiedName, Message.class);
+ }
+
+ private URI uriOfMessageWithName(String name) {
+ XtextResource resource = xtext.resource();
+ Message message = xtext.find(name, Message.class);
+ String fragment = resource.getURIFragment(message);
+ return resource.getURI().appendFragment(fragment);
+ }
+
+ private IResourceDescription descriptionOf(Resource resource) {
+ index.setContext(resource);
+ return index.getResourceDescription(resource.getURI());
+ }
+}
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 695d380..35df4c4 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
@@ -20,7 +20,7 @@
private final QualifiedName qualifiedName;
private final Class<? extends EObject> type;
- CppToProtobufMapping(QualifiedName qualifiedName, Class<? extends EObject> type) {
+ public CppToProtobufMapping(QualifiedName qualifiedName, Class<? extends EObject> type) {
this.qualifiedName = qualifiedName;
this.type = type;
}
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
index 43a0d2c..f7a254b 100644
--- 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
@@ -36,16 +36,19 @@
public URI findUriOfMatchingProtobufElement(IResourceDescription resource, CppToProtobufMapping mapping) {
QualifiedName qualifiedName = mapping.qualifiedName();
Pattern pattern = patternToMatchFrom(qualifiedName);
- List<IEObjectDescription> matches = descriptions.matchingQualifiedNames(resource, pattern);
- if (matches.size() == 1) {
+ List<IEObjectDescription> matches = descriptions.matchingQualifiedNames(resource, pattern, mapping.type());
+ if (!matches.isEmpty()) {
return matches.get(0).getEObjectURI();
}
return null;
}
- Pattern patternToMatchFrom(QualifiedName qualifiedName) {
+ private Pattern patternToMatchFrom(QualifiedName qualifiedName) {
String qualifiedNameAsText = converter.toString(qualifiedName);
- String regex = Pattern.quote(qualifiedNameAsText.replaceAll("_", "."));
+ // escape existing "."
+ // replace "_" with "(\.|_)"
+ String regex = qualifiedNameAsText.replaceAll("\\.", "\\\\.")
+ .replaceAll("_", "\\(\\\\.|_\\)");
return Pattern.compile(regex);
}
}
diff --git a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/IndexLookup_resourceIn_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/resource/IndexLookup_resourceIn_Test.java
similarity index 93%
rename from com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/IndexLookup_resourceIn_Test.java
rename to com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/resource/IndexLookup_resourceIn_Test.java
index a6cb9b5..1936dc8 100644
--- a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/IndexLookup_resourceIn_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/resource/IndexLookup_resourceIn_Test.java
@@ -8,7 +8,7 @@
*/
package com.google.eclipse.protobuf.resource;
-import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
+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.*;
@@ -28,7 +28,7 @@
* @author alruiz@google.com (Alex Ruiz)
*/
public class IndexLookup_resourceIn_Test {
- @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
+ @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
@Inject private IndexLookup lookup;
diff --git a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/ResourceDescriptions_modelObjectUri_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/resource/ResourceDescriptions_modelObjectUri_Test.java
similarity index 77%
rename from com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/ResourceDescriptions_modelObjectUri_Test.java
rename to com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/resource/ResourceDescriptions_modelObjectUri_Test.java
index 0d680f5..a566616 100644
--- a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/resource/ResourceDescriptions_modelObjectUri_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/resource/ResourceDescriptions_modelObjectUri_Test.java
@@ -8,7 +8,7 @@
*/
package com.google.eclipse.protobuf.resource;
-import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
+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.*;
@@ -30,13 +30,12 @@
* @author alruiz@google.com (Alex Ruiz)
*/
public class ResourceDescriptions_modelObjectUri_Test {
- @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
+ @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
@Inject private IQualifiedNameConverter fqnConverter;
@Inject private ResourceSetBasedResourceDescriptions index;
@Inject private ResourceDescriptions resourceDescriptions;
-
// syntax = "proto2";
// package com.google.proto;
//
@@ -45,13 +44,16 @@
// TWO = 2;
// }
@Test public void should_find_URI_of_model_object_given_its_qualified_name() {
- XtextResource resource = xtext.resource();
QualifiedName qualifiedName = fqnConverter.toQualifiedName("com.google.proto.Type");
- URI foundUri = resourceDescriptions.modelObjectUri(describe(resource), qualifiedName);
+ URI foundUri = resourceDescriptions.modelObjectUri(descriptionOf(xtext.resource()), qualifiedName);
+ assertThat(foundUri, equalTo(uriOfEnumWithName("Type")));
+ }
+
+ private URI uriOfEnumWithName(String name) {
+ XtextResource resource = xtext.resource();
Enum anEnum = xtext.find("Type", Enum.class);
String fragment = resource.getURIFragment(anEnum);
- URI expectedUri = resource.getURI().appendFragment(fragment);
- assertThat(foundUri, equalTo(expectedUri));
+ return resource.getURI().appendFragment(fragment);
}
// syntax = "proto2";
@@ -60,11 +62,11 @@
// message Person {}
@Test public void should_return_null_if_file_name_is_equal_but_file_path_is_not() {
QualifiedName qualifiedName = fqnConverter.toQualifiedName("com.google.proto.Type");
- URI foundUri = resourceDescriptions.modelObjectUri(describe(xtext.resource()), qualifiedName);
+ URI foundUri = resourceDescriptions.modelObjectUri(descriptionOf(xtext.resource()), qualifiedName);
assertNull(foundUri);
}
- private IResourceDescription describe(Resource resource) {
+ private IResourceDescription descriptionOf(Resource resource) {
index.setContext(resource);
return index.getResourceDescription(resource.getURI());
}
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 b0416fb..b28ffe3 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,6 +8,7 @@
*/
package com.google.eclipse.protobuf.resource;
+import static com.google.common.base.Objects.equal;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.unmodifiableList;
@@ -15,6 +16,7 @@
import java.util.regex.*;
import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.*;
import org.eclipse.xtext.naming.*;
import org.eclipse.xtext.resource.*;
@@ -29,10 +31,10 @@
@Inject private IQualifiedNameConverter converter;
/**
- * Finds the URI of a model object in the given resource whose qualified name matches the given one.
+ * Finds the URI of a model object, in the given resource, whose qualified name matches the given one.
* @param resource the given resource.
* @param qualifiedName the qualified name to match.
- * @return the URI of the found model object, or {@code null} if a model object with a matching URI could not be
+ * @return the URI of the matching model object, or {@code null} if a model object with a matching URI could not be
* found.
*/
public URI modelObjectUri(IResourceDescription resource, QualifiedName qualifiedName) {
@@ -45,15 +47,31 @@
return null;
}
- public List<IEObjectDescription> matchingQualifiedNames(IResourceDescription resource, Pattern pattern) {
+ /**
+ * Finds the URIs of the model objects, in the given resource, that:
+ * <ol>
+ * <li>have a qualified name that match the given pattern, and</li>
+ * <li>have an {@code EClass} whose name is equal to the simple name of the given type.
+ * </ol>
+ * @param resource the given resource.
+ * @param pattern the pattern to match.
+ * @param type the type of model object we are looking for.
+ * @return the URI of the matching object models, or an empty list if matches could not be found.
+ */
+ public List<IEObjectDescription> matchingQualifiedNames(IResourceDescription resource, Pattern pattern,
+ Class<? extends EObject> type) {
List<IEObjectDescription> descriptions = newArrayList();
for (IEObjectDescription exported : resource.getExportedObjects()) {
QualifiedName qualifiedName = exported.getQualifiedName();
Matcher matcher = pattern.matcher(converter.toString(qualifiedName));
- if (matcher.matches()) {
+ if (matcher.matches() && haveMatchingNames(exported.getEClass(), type)) {
descriptions.add(exported);
}
}
return unmodifiableList(descriptions);
}
+
+ private boolean haveMatchingNames(EClass eClass, Class<? extends EObject> type) {
+ return equal(eClass.getName(), type.getSimpleName());
+ }
}