Fixed: [Issue 169] "Extend" can also extend group.
diff --git a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/bugs/Issue169_AddSupportForExtendingGroups_Test.java b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/bugs/Issue169_AddSupportForExtendingGroups_Test.java
new file mode 100644
index 0000000..a48fa5a
--- /dev/null
+++ b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/bugs/Issue169_AddSupportForExtendingGroups_Test.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2011 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.bugs;
+
+import static com.google.eclipse.protobuf.junit.core.Setups.integrationTestSetup;
+import static com.google.eclipse.protobuf.junit.core.XtextRule.createWith;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
+
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.*;
+
+import org.junit.*;
+
+/**
+ * Tests fix for <a href="http://code.google.com/p/protobuf-dt/issues/detail?id=169">Issue 169</a>.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class Issue169_AddSupportForExtendingGroups_Test {
+
+ @Rule public XtextRule xtext = createWith(integrationTestSetup());
+
+ // // Create file types.proto
+ //
+ // syntax = 'proto2';
+ // package google.proto.test;
+ //
+ // message TopMessage {
+ // optional group MidGroup = 1 {
+ // optional group BottomGroup = 2 {}
+ // }
+ // }
+
+ // syntax = "proto2";
+ // package com.google.proto.project.shared;
+ //
+ // import "types.proto";
+ //
+ // extend .google.proto.test.TopMessage.MidGroup.BottomGroup {}
+ @Test public void should_extend_group_inside_group() {
+ ExtensibleTypeLink link = xtext.find("BottomGroup", " {", ExtensibleTypeLink.class);
+ Group group = (Group) link.getTarget();
+ assertThat(group.getName(), equalTo("BottomGroup"));
+ }
+
+ // // Create file types.proto
+ //
+ // syntax = 'proto2';
+ // package google.proto.test;
+ //
+ // message TopMessage {
+ // message MidMessage {
+ // optional group BottomGroup = 1 {}
+ // }
+ // }
+
+ // syntax = "proto2";
+ // package com.google.proto.project.shared;
+ //
+ // import "types.proto";
+ //
+ // extend .google.proto.test.TopMessage.MidMessage.BottomGroup {}
+ @Test public void should_extend_group_inside_message() {
+ ExtensibleTypeLink link = xtext.find("BottomGroup", " {", ExtensibleTypeLink.class);
+ Group group = (Group) link.getTarget();
+ assertThat(group.getName(), equalTo("BottomGroup"));
+ }
+
+ // // Create file types.proto
+ //
+ // syntax = 'proto2';
+ // package google.proto.test;
+ //
+ // message TopMessage {
+ // optional group MidGroup = 1 {
+ // message BottomMessage {}
+ // }
+ // }
+
+ // syntax = "proto2";
+ // package com.google.proto.project.shared;
+ //
+ // import "types.proto";
+ //
+ // extend .google.proto.test.TopMessage.MidGroup.BottomMessage {}
+ @Test public void should_extend_message_inside_group() {
+ ExtensibleTypeLink link = xtext.find("BottomMessage", " {", ExtensibleTypeLink.class);
+ Message message = (Message) link.getTarget();
+ assertThat(message.getName(), equalTo("BottomMessage"));
+ }
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelFinder_localExtensionsFrom_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelFinder_localExtensionsFrom_Test.java
index a3a5dee..3832a9e 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelFinder_localExtensionsFrom_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelFinder_localExtensionsFrom_Test.java
@@ -43,8 +43,8 @@
// extend Person {}
@Test public void should_return_extensions_of_message() {
Message m = xtext.find("Person", " {", Message.class);
- List<MessageExtension> extensions = new ArrayList<MessageExtension>(finder.localExtensionsOf(m));
- Message referred = extensions.get(0).getMessage().getTarget();
+ List<TypeExtension> extensions = new ArrayList<TypeExtension>(finder.localExtensionsOf(m));
+ Message referred = (Message) extensions.get(0).getType().getTarget();
assertSame(m, referred);
}
}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelFinder_messageFrom_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelFinder_messageFrom_Test.java
index f51de92..d49dc46 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelFinder_messageFrom_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/ModelFinder_messageFrom_Test.java
@@ -20,7 +20,7 @@
import com.google.eclipse.protobuf.protobuf.*;
/**
- * Tests for <code>{@link ModelFinder#messageFrom(MessageExtension)}</code>.
+ * Tests for <code>{@link ModelFinder#messageFrom(TypeExtension)}</code>.
*
* @author alruiz@google.com (Alex Ruiz)
*/
@@ -42,14 +42,14 @@
//
// extend Person {}
@Test public void should_return_message_from_extension() {
- MessageExtension extension = xtext.find("Person", " {}", MessageExtension.class);
+ TypeExtension extension = xtext.find("Person", " {}", TypeExtension.class);
Message message = finder.messageFrom(extension);
assertThat(message.getName(), equalTo("Person"));
}
@Test public void should_return_null_if_extension_is_not_referring_to_message() {
- MessageExtension extension = mock(MessageExtension.class);
- when(extension.getMessage()).thenReturn(null);
+ TypeExtension extension = mock(TypeExtension.class);
+ when(extension.getType()).thenReturn(null);
assertNull(finder.messageFrom(extension));
}
}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
index b8109f4..1a8d68f 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
@@ -82,13 +82,22 @@
}
}
+ @Override public void completeExtensibleTypeLink_Target(EObject model, Assignment assignment,
+ ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
+ Collection<IEObjectDescription> scope = emptySet();
+ if (model instanceof TypeExtension) {
+ TypeExtension extension = (TypeExtension) model;
+ scope = scoping().allPossibleTypesFor(extension);
+ }
+ for (IEObjectDescription d : descriptionChooser.shortestQualifiedNamesIn(scope)) {
+ Image image = imageHelper.getImage(images.imageFor(d.getEObjectOrProxy()));
+ proposeAndAccept(d, image, context, acceptor);
+ }
+ }
+
@Override public void completeMessageLink_Target(EObject model, Assignment assignment, ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
Collection<IEObjectDescription> scope = emptySet();
- if (model instanceof MessageExtension) {
- MessageExtension extension = (MessageExtension) model;
- scope = scoping().allPossibleMessagesFor(extension);
- }
if (model instanceof Rpc) {
Rpc rpc = (Rpc) model;
scope = scoping().allPossibleMessagesFor(rpc);
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java
index c7d940b..c9ecbdb 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java
@@ -55,8 +55,8 @@
highlight((ComplexType) element, acceptor);
continue;
}
- if (element instanceof MessageExtension) {
- highlight((MessageExtension) element, acceptor);
+ if (element instanceof TypeExtension) {
+ highlight((TypeExtension) element, acceptor);
continue;
}
if (element instanceof Service) {
@@ -65,8 +65,8 @@
}
}
- private void highlight(MessageExtension extension, IHighlightedPositionAcceptor acceptor) {
- highlightFirstFeature(extension, MESSAGE_EXTENSION__MESSAGE, acceptor, MESSAGE_ID);
+ private void highlight(TypeExtension extension, IHighlightedPositionAcceptor acceptor) {
+ highlightFirstFeature(extension, TYPE_EXTENSION__TYPE, acceptor, MESSAGE_ID);
for (MessageElement element : extension.getElements()) {
highlight(element, acceptor);
}
@@ -102,8 +102,8 @@
highlight((ComplexType) element, acceptor);
return;
}
- if (element instanceof MessageExtension) {
- highlight((MessageExtension) element, acceptor);
+ if (element instanceof TypeExtension) {
+ highlight((TypeExtension) element, acceptor);
}
}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Images.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Images.java
index 25e2b12..bc3d548 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Images.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Images.java
@@ -41,7 +41,7 @@
private static final Map<Class<?>, String> IMAGES_BY_TYPE = new HashMap<Class<?>, String>();
static {
IMAGES_BY_TYPE.put(Enum.class, "enum.gif");
- IMAGES_BY_TYPE.put(MessageExtension.class, "extend.gif");
+ IMAGES_BY_TYPE.put(TypeExtension.class, "extend.gif");
IMAGES_BY_TYPE.put(Extensions.class, "extensions.gif");
IMAGES_BY_TYPE.put(Group.class, "group.gif");
IMAGES_BY_TYPE.put(Import.class, "import.gif");
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java
index 579a71f..516633f 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java
@@ -49,8 +49,8 @@
Literal literal = (Literal) o;
return labelFor(literal);
}
- if (o instanceof MessageExtension) {
- MessageExtension extend = (MessageExtension) o;
+ if (o instanceof TypeExtension) {
+ TypeExtension extend = (TypeExtension) o;
return labelFor(extend);
}
if (o instanceof MessageField) {
@@ -100,8 +100,14 @@
return text;
}
- private Object labelFor(MessageExtension extension) {
- return messageName(extension.getMessage());
+ private Object labelFor(TypeExtension extension) {
+ return typeName(extension.getType());
+ }
+
+ private String typeName(ExtensibleTypeLink link) {
+ ExtensibleType type = link.getTarget();
+ if (type == null) return null;
+ return nameResolver.nameOf(type);
}
private Object labelFor(MessageField field) {
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
index a620330..415e8d4 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
@@ -21,7 +21,7 @@
'syntax' '=' name=STRING ';';
ProtobufElement:
- Package | Import | Option | ComplexType | MessageExtension | Service;
+ Package | Import | Option | ComplexType | TypeExtension | Service;
Package:
'package' name=PackageName ';';
@@ -39,7 +39,7 @@
'import' 'public' importURI=STRING ';';
ComplexType:
- Message | Enum | Group;
+ Enum | ExtensibleType;
Message:
'message' name=Name '{'
@@ -47,7 +47,7 @@
'}' (';')?;
MessageElement:
- Option | Extensions | ComplexType | MessageField | MessageExtension;
+ Option | Extensions | ComplexType | MessageField | TypeExtension;
Range:
from=LONG ('to' to=RangeMax)?;
@@ -62,7 +62,7 @@
'}' (';')?;
GroupElement:
- Option | IndexedElement | ComplexType | MessageExtension | Extensions;
+ Option | IndexedElement | ComplexType | TypeExtension | Extensions;
Extensions:
'extensions' ranges+=Range (',' ranges+=Range)* ';';
@@ -102,11 +102,17 @@
terminal HEX returns ecore::ELong:
('-')? '0x' (NUMBER | 'a'..'f' | 'A'..'F')+;
-MessageExtension:
- =>'extend' message=MessageLink '{'
+TypeExtension:
+ =>'extend' type=ExtensibleTypeLink '{'
elements+=MessageElement*
'}' (';')?;
+ExtensibleTypeLink:
+ target=[ExtensibleType|QualifiedName];
+
+ExtensibleType:
+ Message | Group;
+
Service:
'service' name=Name '{'
(elements+=ServiceElement)*
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/ModelFinder.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/ModelFinder.java
index b6783e7..ef43699 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/ModelFinder.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/ModelFinder.java
@@ -38,13 +38,13 @@
* @param message the given message.
* @return all the <strong>local</strong> extensions of the given message, or an empty collection if none is found.
*/
- public Collection<MessageExtension> localExtensionsOf(Message message) {
+ public Collection<TypeExtension> localExtensionsOf(Message message) {
return extensionsOf(message, rootOf(message));
}
- public Collection<MessageExtension> extensionsOf(Message message, Protobuf root) {
- Set<MessageExtension> extensions = new HashSet<MessageExtension>();
- for (MessageExtension extension : getAllContentsOfType(root, MessageExtension.class)) {
+ public Collection<TypeExtension> extensionsOf(Message message, Protobuf root) {
+ Set<TypeExtension> extensions = new HashSet<TypeExtension>();
+ for (TypeExtension extension : getAllContentsOfType(root, TypeExtension.class)) {
Message referred = messageFrom(extension);
if (message.equals(referred)) extensions.add(extension);
}
@@ -56,9 +56,11 @@
* @param extension the given extension.
* @return the message from the given extension, or {@code null} if the extension is not referring to a message.
*/
- public Message messageFrom(MessageExtension extension) {
- MessageLink link = extension.getMessage();
- return link == null ? null : link.getTarget();
+ public Message messageFrom(TypeExtension extension) {
+ ExtensibleTypeLink link = extension.getType();
+ if (link == null) return null;
+ ExtensibleType type = link.getTarget();
+ return (type instanceof Message) ? (Message) type : null;
}
/**
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/AstWalker.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/AstWalker.java
index 62db31a..aab7e7c 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/AstWalker.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/AstWalker.java
@@ -61,7 +61,7 @@
Set<IEObjectDescription> descriptions = new HashSet<IEObjectDescription>();
for (EObject element : start.eContents()) {
descriptions.addAll(scopeFinder.local(element, criteria, level));
- if (element instanceof Message) {
+ if (element instanceof Message || element instanceof Group) {
descriptions.addAll(local(element, scopeFinder, criteria, level + 1));
}
}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionFieldScopeFinder.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionFieldScopeFinder.java
index e128e33..00ab0d6 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionFieldScopeFinder.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionFieldScopeFinder.java
@@ -119,7 +119,7 @@
Message fieldType = modelFinder.messageTypeOf((MessageField) e);
if (fieldType == null) return emptyList();
Set<IEObjectDescription> descriptions = new HashSet<IEObjectDescription>();
- for (MessageExtension extension : modelFinder.localExtensionsOf(fieldType)) {
+ for (TypeExtension extension : modelFinder.localExtensionsOf(fieldType)) {
for (MessageElement element : extension.getElements()) {
if (!(element instanceof IndexedElement)) continue;
IndexedElement current = (IndexedElement) element;
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionScopeFinder.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionScopeFinder.java
index d3df720..7089de5 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionScopeFinder.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionScopeFinder.java
@@ -36,7 +36,7 @@
OptionType optionType = optionTypeFrom(criteria);
if (!isExtendingOptionMessage(target, optionType)) return emptySet();
Set<IEObjectDescription> descriptions = new HashSet<IEObjectDescription>();
- MessageExtension extension = (MessageExtension) target;
+ TypeExtension extension = (TypeExtension) target;
for (MessageElement e : extension.getElements()) {
descriptions.addAll(qualifiedNamesDescriptions.qualifiedNamesForOption(e));
}
@@ -51,7 +51,7 @@
OptionType optionType = optionTypeFrom(criteria);
if (!isExtendingOptionMessage(target, optionType)) return emptySet();
Set<IEObjectDescription> descriptions = new HashSet<IEObjectDescription>();
- MessageExtension extension = (MessageExtension) target;
+ TypeExtension extension = (TypeExtension) target;
for (MessageElement e : extension.getElements()) {
List<QualifiedName> names = localNamesProvider.namesForOption(e);
int nameCount = names.size();
@@ -70,8 +70,8 @@
}
private boolean isExtendingOptionMessage(Object o, OptionType optionType) {
- if (!(o instanceof MessageExtension)) return false;
- Message message = modelFinder.messageFrom((MessageExtension) o);
+ if (!(o instanceof TypeExtension)) return false;
+ Message message = modelFinder.messageFrom((TypeExtension) o);
if (message == null) return false;
String name = message.getName();
return optionType.messageName().equals(name);
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/FieldNotationScopeFinder.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/FieldNotationScopeFinder.java
index 39270d8..aabaaa8 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/FieldNotationScopeFinder.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/FieldNotationScopeFinder.java
@@ -81,7 +81,7 @@
Set<IEObjectDescription> descriptions = new HashSet<IEObjectDescription>();
Message fieldType = modelFinder.messageTypeOf(field);
// check first in descriptor.proto
- for (MessageExtension extension : modelFinder.extensionsOf(fieldType, modelFinder.rootOf(field))) {
+ for (TypeExtension extension : modelFinder.extensionsOf(fieldType, modelFinder.rootOf(field))) {
for (MessageElement element : extension.getElements()) {
if (!(element instanceof MessageField)) continue;
descriptions.addAll(qualifiedNameDescriptions.qualifiedNames(element));
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
index 7cad285..28a1eb3 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
@@ -61,12 +61,22 @@
}
@SuppressWarnings("unused")
- public IScope scope_MessageLink_target(MessageLink link, EReference r) {
- return createScope(messagesFor(link));
+ public IScope scope_ExtensibleTypeLink_target(ExtensibleTypeLink link, EReference r) {
+ return createScope(extensibleTypesFor(link));
}
- @Override public Collection<IEObjectDescription> allPossibleMessagesFor(MessageExtension extension) {
- return messagesFor(extension);
+ @Override public Collection<IEObjectDescription> allPossibleTypesFor(TypeExtension extension) {
+ return extensibleTypesFor(extension);
+ }
+
+ private Collection<IEObjectDescription> extensibleTypesFor(EObject o) {
+ Protobuf root = modelFinder.rootOf(o);
+ return astWalker.traverseAst(root, typeScopeFinder, ExtensibleType.class);
+ }
+
+ @SuppressWarnings("unused")
+ public IScope scope_MessageLink_target(MessageLink link, EReference r) {
+ return createScope(messagesFor(link));
}
@Override public Collection<IEObjectDescription> allPossibleMessagesFor(Rpc rpc) {
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/Scoping.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/Scoping.java
index 9f9c425..b35c088 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/Scoping.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/Scoping.java
@@ -21,7 +21,7 @@
Collection<IEObjectDescription> allPossibleTypesFor(MessageField field);
- Collection<IEObjectDescription> allPossibleMessagesFor(MessageExtension extension);
+ Collection<IEObjectDescription> allPossibleTypesFor(TypeExtension extension);
Collection<IEObjectDescription> allPossibleMessagesFor(Rpc rpc);