Working on supporting pluggable descriptor.proto definitions.
diff --git a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/util/Strings_firstCharToLowerCase_Test.java b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/util/Strings_firstCharToLowerCase_Test.java index 1b4b7a4..6453372 100644 --- a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/util/Strings_firstCharToLowerCase_Test.java +++ b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/util/Strings_firstCharToLowerCase_Test.java
@@ -22,21 +22,15 @@ */ public class Strings_firstCharToLowerCase_Test { - private static Strings strings; - - @BeforeClass public static void setUpOnce() { - strings = new Strings(); - } - @Test public void should_return_null() { - assertThat(strings.firstCharToLowerCase(null), nullValue()); + assertThat(Strings.firstCharToLowerCase(null), nullValue()); } @Test public void should_return_empty_String() { - assertThat(strings.firstCharToLowerCase(""), equalTo("")); + assertThat(Strings.firstCharToLowerCase(""), equalTo("")); } @Test public void should_return_String_with_first_char_lower_case() { - assertThat(strings.firstCharToLowerCase("HElLo"), equalTo("hElLo")); + assertThat(Strings.firstCharToLowerCase("HElLo"), equalTo("hElLo")); } }
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 a7f3e19..845fa54 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
@@ -12,6 +12,7 @@ import static com.google.eclipse.protobuf.protobuf.ScalarType.STRING; import static com.google.eclipse.protobuf.ui.grammar.CommonKeyword.*; import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.*; +import static com.google.eclipse.protobuf.ui.util.Strings.firstCharToLowerCase; import static java.lang.String.valueOf; import static java.util.Collections.emptyList; @@ -54,7 +55,6 @@ @Inject private Images images; @Inject private Literals literals; @Inject private Properties properties; - @Inject private Strings strings; /** {@inheritDoc} */ @Override public void completeProtobuf_Syntax(EObject model, Assignment assignment, ContentAssistContext context, @@ -310,7 +310,7 @@ @Override public void completeProperty_Name(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { - String typeName = strings.firstCharToLowerCase(properties.typeNameOf((Property) model)); + String typeName = firstCharToLowerCase(properties.typeNameOf((Property) model)); int index = 1; String name = typeName + index; for (EObject o : model.eContainer().eContents()) {
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 8993736..c02e9ec 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
@@ -8,10 +8,12 @@ */ package com.google.eclipse.protobuf.ui.editor.syntaxcoloring; -import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.findNodesForFeature; import static org.eclipse.xtext.ui.editor.syntaxcoloring.DefaultHighlightingConfiguration.DEFAULT_ID; -import java.util.List; +import com.google.eclipse.protobuf.protobuf.*; +import com.google.eclipse.protobuf.protobuf.Package; +import com.google.eclipse.protobuf.util.ModelNodes; +import com.google.inject.Inject; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.*; @@ -19,14 +21,13 @@ import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.ui.editor.syntaxcoloring.*; -import com.google.eclipse.protobuf.protobuf.*; -import com.google.eclipse.protobuf.protobuf.Package; - /** * @author alruiz@google.com (Alex Ruiz) */ public class ProtobufSemanticHighlightingCalculator implements ISemanticHighlightingCalculator { + @Inject private ModelNodes nodes; + public void provideHighlightingFor(XtextResource resource, IHighlightedPositionAcceptor acceptor) { if (resource == null) return; EList<EObject> contents = resource.getContents(); @@ -104,13 +105,8 @@ private void highlightFirstFeature(EObject semantic, EStructuralFeature feature, String highlightId, IHighlightedPositionAcceptor acceptor) { - INode node = firstFeatureNode(semantic, feature); + INode node = nodes.firstNodeForFeature(semantic, feature); if (node == null || node.getText() == null) return; acceptor.addPosition(node.getOffset(), node.getText().length(), highlightId); } - - public INode firstFeatureNode(EObject semantic, EStructuralFeature feature) { - List<INode> nodes = findNodesForFeature(semantic, feature); - return (nodes.size() == 1) ? nodes.get(0) : null; - } }
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 496ffd4..d8319e4 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
@@ -8,19 +8,16 @@ */ package com.google.eclipse.protobuf.ui.labeling; +import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__IMPORT_URI; import static org.eclipse.jface.viewers.StyledString.DECORATIONS_STYLER; -import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.findNodesForFeature; -import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.*; - -import org.eclipse.jface.viewers.StyledString; -import org.eclipse.xtext.nodemodel.INode; - -import java.util.List; import com.google.eclipse.protobuf.protobuf.*; import com.google.eclipse.protobuf.ui.util.Properties; -import com.google.inject.Inject; -import com.google.inject.Singleton; +import com.google.eclipse.protobuf.util.ModelNodes; +import com.google.inject.*; + +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.xtext.nodemodel.INode; /** * Registry of commonly used text in the 'Protocol Buffer' editor. @@ -30,6 +27,7 @@ @Singleton public class Labels { + @Inject private ModelNodes nodes; @Inject private Properties properties; public Object labelFor(Object o) { @@ -61,9 +59,8 @@ } private Object labelFor(Import i) { - List<INode> nodes = findNodesForFeature(i, IMPORT__IMPORT_URI); - if (nodes.size() != 1) return i.getImportURI(); - INode node = nodes.get(0); + INode node = nodes.firstNodeForFeature(i, IMPORT__IMPORT_URI); + if (node == null) return i.getImportURI(); return node.getText(); } @@ -77,7 +74,7 @@ private Object labelFor(Property p) { StyledString text = new StyledString(p.getName()); String typeName = properties.typeNameOf(p); - if (typeName == null) typeName = "<unable to resolve type reference>"; + if (typeName == null) typeName = "<unresolved reference>"; // TODO move to properties file String indexAndType = String.format(" [%d] : %s", p.getIndex(), typeName); text.append(indexAndType, DECORATIONS_STYLER); return text;
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Fields.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Fields.java index a9e8d40..47e35fd 100644 --- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Fields.java +++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Fields.java
@@ -38,7 +38,7 @@ * * The calculated tag number value for the field {@code PhoneNumber} will be 3. * </p> - * @param p the given field. + * @param f the given field. * @return the calculated value for the tag number of the given field. */ public int calculateTagNumberOf(Field f) {
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Strings.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Strings.java index 4b66918..af8b091 100644 --- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Strings.java +++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Strings.java
@@ -10,14 +10,11 @@ import static java.lang.Character.toLowerCase; -import com.google.inject.Singleton; - /** * Utility methods related to {@code String}. * * @author alruiz@google.com (Alex Ruiz) */ -@Singleton public class Strings { /** Pattern to split CSVs. */ @@ -28,11 +25,13 @@ * @param s the original {@code String} * @return a {@code String} with the same content as the given one, but with the first character in lower case. */ - public String firstCharToLowerCase(String s) { + public static String firstCharToLowerCase(String s) { if (s == null) return null; if (s.length() == 0) return s; char[] chars = s.toCharArray(); chars[0] = toLowerCase(chars[0]); return new String(chars); } + + private Strings() {} }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java index 2b9a399..b9ecf9a 100644 --- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java
@@ -8,24 +8,28 @@ */ package com.google.eclipse.protobuf.scoping; +import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.PROPERTY__TYPE; import static com.google.eclipse.protobuf.scoping.OptionType.*; import static com.google.eclipse.protobuf.util.Closeables.close; import static java.util.Collections.unmodifiableCollection; import static org.eclipse.emf.common.util.URI.createURI; import static org.eclipse.xtext.EcoreUtil2.*; import static org.eclipse.xtext.util.CancelIndicator.NullImpl; +import static org.eclipse.xtext.util.Strings.isEmpty; + +import com.google.eclipse.protobuf.protobuf.*; +import com.google.eclipse.protobuf.protobuf.Enum; +import com.google.eclipse.protobuf.util.ModelNodes; +import com.google.inject.Inject; + +import org.eclipse.xtext.nodemodel.INode; +import org.eclipse.xtext.parser.*; +import org.eclipse.xtext.resource.XtextResource; import java.io.*; import java.net.URL; import java.util.*; -import org.eclipse.xtext.parser.*; -import org.eclipse.xtext.resource.XtextResource; - -import com.google.eclipse.protobuf.protobuf.*; -import com.google.eclipse.protobuf.protobuf.Enum; -import com.google.inject.Inject; - /** * Contains the elements from descriptor.proto (provided with protobuf's library.) * @@ -34,13 +38,23 @@ public class ProtoDescriptor implements IProtoDescriptor { private static final String DESCRIPTOR_URI = "platform:/plugin/com.google.eclipse.protobuf/descriptor.proto"; - - private final Map<OptionType, Map<String, Property>> options = new HashMap<OptionType, Map<String, Property>>(); - private final Map<String, Enum> enums = new HashMap<String, Enum>(); + private static final Map<String, OptionType> OPTION_DEFINITION_BY_NAME = new HashMap<String, OptionType>(); + + static { + OPTION_DEFINITION_BY_NAME.put("FileOptions", FILE); + OPTION_DEFINITION_BY_NAME.put("MessageOptions", MESSAGE); + OPTION_DEFINITION_BY_NAME.put("FieldOptions", FIELD); + } + + private final Map<OptionType, Map<String, Property>> optionsByType = new HashMap<OptionType, Map<String, Property>>(); + private final Map<String, Enum> enumsByName = new HashMap<String, Enum>(); private Protobuf root; - @Inject public ProtoDescriptor(IParser parser) { + private final ModelNodes nodes; + + @Inject public ProtoDescriptor(IParser parser, ModelNodes nodes) { + this.nodes = nodes; addOptionTypes(); InputStreamReader reader = null; try { @@ -61,7 +75,7 @@ private void addOptionTypes() { for (OptionType type : OptionType.values()) - options.put(type, new LinkedHashMap<String, Property>()); + optionsByType.put(type, new LinkedHashMap<String, Property>()); } private static InputStream globalScopeContents() throws IOException { @@ -71,86 +85,34 @@ private void initContents() { for (Message m : getAllContentsOfType(root, Message.class)) { - if (isFileOptionsMessage(m)) initFileOptions(m); - else if (isMessageOptionsMessage(m)) initMessageOptions(m); - else if (isFieldOptionsMessage(m)) initFieldOptions(m); + OptionType type = OPTION_DEFINITION_BY_NAME.get(m.getName()); + if (type == null) continue; + initOptions(m, type); } } - private boolean isFileOptionsMessage(Message m) { - return "FileOptions".equals(m.getName()); - } - - private boolean isMessageOptionsMessage(Message m) { - return "MessageOptions".equals(m.getName()); - } - - private boolean isFieldOptionsMessage(Message m) { - return "FieldOptions".equals(m.getName()); - } - - private void initFileOptions(Message fileOptionsMessage) { - for (MessageElement e : fileOptionsMessage.getElements()) { + private void initOptions(Message optionGroup, OptionType type) { + for (MessageElement e : optionGroup.getElements()) { if (e instanceof Property) { - addFileOption((Property) e); + addOption((Property) e, type); continue; } - if (isEnumWithName(e, "OptimizeMode")) { - enums.put("optimize_for", (Enum) e); - continue; + if (e instanceof Enum) { + Enum anEnum = (Enum) e; + enumsByName.put(anEnum.getName(), anEnum); } } } - private void addFileOption(Property p) { - addOption(FILE, p); - } - - private void initMessageOptions(Message messageOptionsMessage) { - for (MessageElement e : messageOptionsMessage.getElements()) { - if (e instanceof Property) { - addMessageOption((Property) e); - continue; - } - } - } - - private void addMessageOption(Property p) { - addOption(MESSAGE, p); - } - - private void initFieldOptions(Message fieldOptionsMessage) { - for (MessageElement e : fieldOptionsMessage.getElements()) { - if (e instanceof Property) { - addFieldOption((Property) e); - continue; - } - if (isEnumWithName(e, "CType")) { - enums.put("ctype", (Enum) e); - continue; - } - } - } - - private void addFieldOption(Property p) { - addOption(FIELD, p); - } - - private void addOption(OptionType type, Property p) { - if (shouldIgnore(p)) return; - options.get(type).put(p.getName(), p); + private void addOption(Property optionDefinition, OptionType type) { + if (shouldIgnore(optionDefinition)) return; + optionsByType.get(type).put(optionDefinition.getName(), optionDefinition); } private boolean shouldIgnore(Property property) { return "uninterpreted_option".equals(property.getName()); } - private boolean isEnumWithName(MessageElement e, String name) { - if (!(e instanceof Enum)) return false; - Enum anEnum = (Enum) e; - return name.equals(anEnum.getName()); - } - /** {@inheritDoc} */ public Collection<Property> fileOptions() { return optionsOfType(FILE); @@ -158,9 +120,24 @@ /** {@inheritDoc} */ public Property lookupOption(String name) { - Property p = lookupOption(FILE, name); - if (p == null) lookupOption(MESSAGE, name); - return p; + return lookupOption(name, FILE, MESSAGE); + } + + public Property lookupOption(String name, OptionType...types) { + for (OptionType type : types) { + Property p = lookupOption(name, type); + if (p != null) return p; + } + return null; + } + + /** {@inheritDoc} */ + public Property lookupFieldOption(String name) { + return lookupOption(name, FIELD); + } + + private Property lookupOption(String name, OptionType type) { + return optionsByType.get(type).get(name); } /** {@inheritDoc} */ @@ -174,25 +151,26 @@ } private Collection<Property> optionsOfType(OptionType type) { - return unmodifiableCollection(options.get(type).values()); - } - - /** {@inheritDoc} */ - public Property lookupFieldOption(String name) { - return lookupOption(FIELD, name); - } - - private Property lookupOption(OptionType type, String name) { - return options.get(type).get(name); + return unmodifiableCollection(optionsByType.get(type).values()); } /** {@inheritDoc} */ public Enum enumTypeOf(Option option) { - return enums.get(option.getName()); + String name = option.getName(); + return enumTypeOf(lookupOption(name)); } /** {@inheritDoc} */ public Enum enumTypeOf(FieldOption option) { - return enums.get(option.getName()); + String name = option.getName(); + return enumTypeOf(lookupFieldOption(name)); + } + + private Enum enumTypeOf(Property p) { + if (p == null) return null; + INode node = nodes.firstNodeForFeature(p, PROPERTY__TYPE); + if (node == null) return null; + String typeName = node.getText(); + return (isEmpty(typeName)) ? null : enumsByName.get(typeName.trim()); } }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptorProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptorProvider.java index 6734b2d..82aef7f 100644 --- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptorProvider.java +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptorProvider.java
@@ -8,10 +8,11 @@ */ package com.google.eclipse.protobuf.scoping; -import org.eclipse.xtext.parser.IParser; - +import com.google.eclipse.protobuf.util.ModelNodes; import com.google.inject.*; +import org.eclipse.xtext.parser.IParser; + /** * Provider of a singleton instance of <code>{@link ProtoDescriptor}</code>. * @@ -21,11 +22,12 @@ public class ProtoDescriptorProvider implements Provider<IProtoDescriptor> { @Inject private IParser parser; + @Inject private ModelNodes nodes; private IProtoDescriptor descriptor; public synchronized IProtoDescriptor get() { - if (descriptor == null) descriptor = new ProtoDescriptor(parser); + if (descriptor == null) descriptor = new ProtoDescriptor(parser, nodes); return descriptor; } }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/ModelNodes.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/ModelNodes.java new file mode 100644 index 0000000..a720813 --- /dev/null +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/ModelNodes.java
@@ -0,0 +1,33 @@ +/* + * 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.util; + +import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.findNodesForFeature; + +import com.google.inject.Singleton; + +import org.eclipse.emf.ecore.*; +import org.eclipse.xtext.nodemodel.INode; + +import java.util.List; + +/** + * Utility methods related to <code>{@link INode}</code>s. + * + * @author alruiz@google.com (Alex Ruiz) + */ +@Singleton +public class ModelNodes { + + public INode firstNodeForFeature(EObject semanticObject, EStructuralFeature structuralFeature) { + List<INode> nodes = findNodesForFeature(semanticObject, structuralFeature); + if (nodes.isEmpty()) return null; + return nodes.get(0); + } +}