From c733efacda319a38e362b998447aa671b4f9d290 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Tue, 27 Sep 2016 11:55:28 -0400 Subject: [PATCH] Java Properties parser project --- .../commons/java-properties/.gitignore | 7 + .../.settings/org.eclipse.jdt.core.prefs | 5 + .../.settings/org.eclipse.m2e.core.prefs | 4 + .../commons/java-properties/pom.xml | 45 ++ .../resources/JavaProperties.g4 | 88 +++ .../properties/antlr/parser/AntlrParser.java | 264 +++++++ .../antlr/parser/JavaProperties.tokens | 10 + .../parser/JavaPropertiesBaseListener.java | 159 ++++ .../antlr/parser/JavaPropertiesLexer.java | 112 +++ .../antlr/parser/JavaPropertiesLexer.tokens | 10 + .../antlr/parser/JavaPropertiesListener.java | 110 +++ .../antlr/parser/JavaPropertiesParser.java | 735 ++++++++++++++++++ .../properties/parser/ParseResults.java | 44 ++ .../ide/vscode/properties/parser/Parser.java | 29 + .../ide/vscode/properties/parser/Problem.java | 45 ++ .../properties/parser/ProblemCodes.java | 24 + .../properties/parser/PropertiesAst.java | 122 +++ .../test/PropertiesAntlrParserTest.java | 222 ++++++ vscode-extensions/commons/pom.xml | 1 + 19 files changed, 2036 insertions(+) create mode 100644 vscode-extensions/commons/java-properties/.gitignore create mode 100644 vscode-extensions/commons/java-properties/.settings/org.eclipse.jdt.core.prefs create mode 100644 vscode-extensions/commons/java-properties/.settings/org.eclipse.m2e.core.prefs create mode 100644 vscode-extensions/commons/java-properties/pom.xml create mode 100644 vscode-extensions/commons/java-properties/resources/JavaProperties.g4 create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/AntlrParser.java create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaProperties.tokens create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesBaseListener.java create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesLexer.java create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesLexer.tokens create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesListener.java create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesParser.java create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/ParseResults.java create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/Parser.java create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/Problem.java create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/ProblemCodes.java create mode 100644 vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/PropertiesAst.java create mode 100644 vscode-extensions/commons/java-properties/src/test/java/org/springframework/ide/vscode/parser/test/PropertiesAntlrParserTest.java diff --git a/vscode-extensions/commons/java-properties/.gitignore b/vscode-extensions/commons/java-properties/.gitignore new file mode 100644 index 000000000..ccff9aa90 --- /dev/null +++ b/vscode-extensions/commons/java-properties/.gitignore @@ -0,0 +1,7 @@ +target +.idea +.project +.classpath +*.iml +.DS_Store +**/.DS_Store \ No newline at end of file diff --git a/vscode-extensions/commons/java-properties/.settings/org.eclipse.jdt.core.prefs b/vscode-extensions/commons/java-properties/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..714351aec --- /dev/null +++ b/vscode-extensions/commons/java-properties/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/vscode-extensions/commons/java-properties/.settings/org.eclipse.m2e.core.prefs b/vscode-extensions/commons/java-properties/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/vscode-extensions/commons/java-properties/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/vscode-extensions/commons/java-properties/pom.xml b/vscode-extensions/commons/java-properties/pom.xml new file mode 100644 index 000000000..c6fad1ece --- /dev/null +++ b/vscode-extensions/commons/java-properties/pom.xml @@ -0,0 +1,45 @@ + + 4.0.0 + java-properties + Java Properties parser, AST and utilities + + + org.springframework.ide.vscode + commons-parent + 0.0.1-SNAPSHOT + ../pom.xml + + + + + + + com.google.guava + guava + 18.0 + + + + org.antlr + antlr4-runtime + 4.5.3 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 1.8 + 1.8 + + + + + + \ No newline at end of file diff --git a/vscode-extensions/commons/java-properties/resources/JavaProperties.g4 b/vscode-extensions/commons/java-properties/resources/JavaProperties.g4 new file mode 100644 index 000000000..5c075b355 --- /dev/null +++ b/vscode-extensions/commons/java-properties/resources/JavaProperties.g4 @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ + +/** + * Based on the answer to this Stack Overflow question: + * http://stackoverflow.com/questions/6132529/antlr-parsing-java-properties + */ + +grammar JavaProperties; + +parse + : line* EOF + ; + +line + : propertyLine + | commentLine + | emptyLine + ; + +propertyLine + : Space* keyValuePair + ; + +commentLine + : Space* Comment (LineBreak | EOF) + ; + +emptyLine + : Space* LineBreak + ; + +keyValuePair + : key separatorAndValue (LineBreak | EOF) + ; + +key + : keyChar+ + ; + +keyChar + : IdentifierChar + | Backslash (Colon | Equals) + ; + +separatorAndValue + : (Space | Colon | Equals) valueChar+ + ; + +valueChar + : IdentifierChar + | Space + | Backslash LineBreak + | Equals + | Colon + ; + +Backslash : '\\'; +Colon : ':'; +Equals : '='; + +Comment + : ('!' | '#') ~('\r' | '\n')* + ; + +LineBreak + : '\r'? '\n' + | '\r' + ; + +Space + : ' ' + | '\t' + | '\f' + ; + +IdentifierChar + : ~([ :=\\\r('\r'? '\n')]) + ; + \ No newline at end of file diff --git a/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/AntlrParser.java b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/AntlrParser.java new file mode 100644 index 000000000..6491a9d5b --- /dev/null +++ b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/AntlrParser.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.properties.antlr.parser; + +import java.util.ArrayList; +import java.util.BitSet; + +import org.antlr.v4.runtime.ANTLRErrorListener; +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.atn.ATNConfigSet; +import org.antlr.v4.runtime.dfa.DFA; +import org.springframework.ide.vscode.properties.antlr.parser.JavaPropertiesParser.CommentLineContext; +import org.springframework.ide.vscode.properties.antlr.parser.JavaPropertiesParser.KeyContext; +import org.springframework.ide.vscode.properties.antlr.parser.JavaPropertiesParser.PropertyLineContext; +import org.springframework.ide.vscode.properties.antlr.parser.JavaPropertiesParser.SeparatorAndValueContext; +import org.springframework.ide.vscode.properties.parser.ParseResults; +import org.springframework.ide.vscode.properties.parser.Parser; +import org.springframework.ide.vscode.properties.parser.Problem; +import org.springframework.ide.vscode.properties.parser.ProblemCodes; +import org.springframework.ide.vscode.properties.parser.PropertiesAst; + +import com.google.common.collect.ImmutableList; + +/** + * ANTLR based parser implementation + * + * @author Alex Boyko + * + */ +public class AntlrParser implements Parser { + + @Override + public ParseResults parse(String text) { + ArrayList syntaxErrors = new ArrayList<>(); + ArrayList problems = new ArrayList<>(); + ArrayList astNodes = new ArrayList<>(); + + JavaPropertiesLexer lexer = new JavaPropertiesLexer(new ANTLRInputStream(text.toCharArray(), text.length())); + CommonTokenStream tokens = new CommonTokenStream(lexer); + JavaPropertiesParser parser = new JavaPropertiesParser(tokens); + + // Add listener to collect various parser errors + parser.addErrorListener(new ANTLRErrorListener() { + + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, + int charPositionInLine, String msg, RecognitionException e) { + syntaxErrors.add(createProblem(msg, ProblemCodes.PROPERTIES_SYNTAX_ERROR, (Token) offendingSymbol)); + } + + @Override + public void reportAmbiguity(org.antlr.v4.runtime.Parser recognizer, DFA dfa, int startIndex, int stopIndex, + boolean exact, BitSet ambigAlts, ATNConfigSet configs) { + problems.add(createProblem("Ambiguity detected!", ProblemCodes.PROPERTIES_AMBIGUITY_ERROR, recognizer.getCurrentToken())); + } + + @Override + public void reportAttemptingFullContext(org.antlr.v4.runtime.Parser recognizer, DFA dfa, int startIndex, + int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) { + problems.add(createProblem("Full-Context attempt detected!", ProblemCodes.PROPERTIES_FULL_CONTEXT_ERROR, recognizer.getCurrentToken())); + } + + @Override + public void reportContextSensitivity(org.antlr.v4.runtime.Parser recognizer, DFA dfa, int startIndex, + int stopIndex, int prediction, ATNConfigSet configs) { + problems.add(createProblem("Context sensitivity detected!", ProblemCodes.PROPERTIES_CONTEXT_SENSITIVITY_ERROR, recognizer.getCurrentToken())); + } + + }); + + // Add listener to the parse tree to collect AST nodes + parser.addParseListener(new JavaPropertiesBaseListener() { + + private Key key = null; + private Value value = null; + + @Override + public void exitPropertyLine(PropertyLineContext ctx) { + astNodes.add(new KeyValuePair(ctx, key, value)); + key = null; + value = null; + } + + @Override + public void exitCommentLine(CommentLineContext ctx) { + astNodes.add(new Comment(ctx)); + } + + @Override + public void exitKey(KeyContext ctx) { + key = new Key(ctx); + } + + @Override + public void exitSeparatorAndValue(SeparatorAndValueContext ctx) { + value = new Value(ctx); + } + + }); + + parser.parse(); + + // Collect and return parse results + return new ParseResults(new PropertiesAst(ImmutableList.copyOf(astNodes)), ImmutableList.copyOf(syntaxErrors), ImmutableList.copyOf(problems)); + } + + private static Problem createProblem(String message, String code, Token token) { + return new Problem() { + + @Override + public String getMessage() { + return message; + } + + @Override + public String getCode() { + return code; + } + + @Override + public int getOffset() { + return token.getStartIndex(); + } + + @Override + public int getLength() { + return token.getStopIndex() - token.getStartIndex(); + } + + }; + } + + private static abstract class Node implements PropertiesAst.Node { + + abstract protected ParserRuleContext getContext(); + + @Override + public int getOffset() { + return getContext().getStart().getStartIndex(); + } + + @Override + public int getLength() { + return getContext().getStop().getStartIndex() - getOffset() + 1; + } + + } + + private static class Comment implements PropertiesAst.Comment { + + private CommentLineContext context; + + public Comment(CommentLineContext context) { + this.context = context; + } + + @Override + public int getOffset() { + return context.Comment().getSymbol().getStartIndex(); + } + + @Override + public int getLength() { + return context.Comment().getSymbol().getStopIndex() - getOffset() + 1; + } + + } + + private static class KeyValuePair extends Node implements PropertiesAst.KeyValuePair { + + private PropertyLineContext context; + private Key key; + private Value value; + + public KeyValuePair(PropertyLineContext context, Key key, Value value) { + this.context = context; + this.key = key; + this.value = value; + } + + protected PropertyLineContext getContext() { + return context; + } + + @Override + public Key getKey() { + return key; + } + + @Override + public Value getValue() { + return value; + } + } + + private static class Key extends Node implements PropertiesAst.Key { + + private KeyContext context; + + public Key(KeyContext context) { + this.context = context; + } + + protected KeyContext getContext() { + return context; + } + + @Override + public String decode() { + return context.getText().replace("\\:", ":").replace("\\=", "="); + } + + } + + private static class Value extends Node implements PropertiesAst.Value { + + private SeparatorAndValueContext context; + private String value; + private String decoded; + + public Value(SeparatorAndValueContext context) { + this.context = context; + init(); + } + + private void init() { + // Remove the separator, if it exists + value = context.getText().replaceAll("^\\s*[:=]?\\s*", ""); + // Remove all escaped line breaks with trailing spaces + decoded = value.replaceAll("\\\\(\r?\n|\r)[ \t\f]*", ""); + } + + @Override + protected SeparatorAndValueContext getContext() { + return context; + } + + @Override + public String decode() { + return decoded; + } + + @Override + public int getOffset() { + // Offset by 1 to skip the separator + return context.getStart().getStartIndex() + (context.getText().length() - value.length()); + } + + } + +} diff --git a/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaProperties.tokens b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaProperties.tokens new file mode 100644 index 000000000..fe8593417 --- /dev/null +++ b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaProperties.tokens @@ -0,0 +1,10 @@ +Backslash=1 +Colon=2 +Equals=3 +Comment=4 +LineBreak=5 +Space=6 +IdentifierChar=7 +'\\'=1 +':'=2 +'='=3 diff --git a/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesBaseListener.java b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesBaseListener.java new file mode 100644 index 000000000..d859c9f2f --- /dev/null +++ b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesBaseListener.java @@ -0,0 +1,159 @@ +// Generated from JavaProperties.g4 by ANTLR 4.5.3 +package org.springframework.ide.vscode.properties.antlr.parser; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * This class provides an empty implementation of {@link JavaPropertiesListener}, + * which can be extended to create a listener which only needs to handle a subset + * of the available methods. + */ +public class JavaPropertiesBaseListener implements JavaPropertiesListener { + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterParse(JavaPropertiesParser.ParseContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitParse(JavaPropertiesParser.ParseContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterLine(JavaPropertiesParser.LineContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitLine(JavaPropertiesParser.LineContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterPropertyLine(JavaPropertiesParser.PropertyLineContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitPropertyLine(JavaPropertiesParser.PropertyLineContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterCommentLine(JavaPropertiesParser.CommentLineContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitCommentLine(JavaPropertiesParser.CommentLineContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterEmptyLine(JavaPropertiesParser.EmptyLineContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitEmptyLine(JavaPropertiesParser.EmptyLineContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterKeyValuePair(JavaPropertiesParser.KeyValuePairContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitKeyValuePair(JavaPropertiesParser.KeyValuePairContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterKey(JavaPropertiesParser.KeyContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitKey(JavaPropertiesParser.KeyContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterKeyChar(JavaPropertiesParser.KeyCharContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitKeyChar(JavaPropertiesParser.KeyCharContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterSeparatorAndValue(JavaPropertiesParser.SeparatorAndValueContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitSeparatorAndValue(JavaPropertiesParser.SeparatorAndValueContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterValueChar(JavaPropertiesParser.ValueCharContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitValueChar(JavaPropertiesParser.ValueCharContext ctx) { } + + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitTerminal(TerminalNode node) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void visitErrorNode(ErrorNode node) { } +} \ No newline at end of file diff --git a/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesLexer.java b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesLexer.java new file mode 100644 index 000000000..cac94e6bb --- /dev/null +++ b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/antlr/parser/JavaPropertiesLexer.java @@ -0,0 +1,112 @@ +// Generated from JavaProperties.g4 by ANTLR 4.5.3 +package org.springframework.ide.vscode.properties.antlr.parser; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.*; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) +public class JavaPropertiesLexer extends Lexer { + static { RuntimeMetaData.checkVersion("4.5.3", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + Backslash=1, Colon=2, Equals=3, Comment=4, LineBreak=5, Space=6, IdentifierChar=7; + public static String[] modeNames = { + "DEFAULT_MODE" + }; + + public static final String[] ruleNames = { + "Backslash", "Colon", "Equals", "Comment", "LineBreak", "Space", "IdentifierChar" + }; + + private static final String[] _LITERAL_NAMES = { + null, "'\\'", "':'", "'='" + }; + private static final String[] _SYMBOLIC_NAMES = { + null, "Backslash", "Colon", "Equals", "Comment", "LineBreak", "Space", + "IdentifierChar" + }; + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + + public JavaPropertiesLexer(CharStream input) { + super(input); + _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @Override + public String getGrammarFileName() { return "JavaProperties.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public String[] getModeNames() { return modeNames; } + + @Override + public ATN getATN() { return _ATN; } + + public static final String _serializedATN = + "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2\t)\b\1\4\2\t\2\4"+ + "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\3\2\3\2\3\3\3\3\3\4\3\4"+ + "\3\5\3\5\7\5\32\n\5\f\5\16\5\35\13\5\3\6\5\6 \n\6\3\6\3\6\5\6$\n\6\3\7"+ + "\3\7\3\b\3\b\2\2\t\3\3\5\4\7\5\t\6\13\7\r\b\17\t\3\2\6\4\2##%%\4\2\f\f"+ + "\17\17\5\2\13\13\16\16\"\"\n\2\f\f\17\17\"\")+<"; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + @Override + public String getGrammarFileName() { return "JavaProperties.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public ATN getATN() { return _ATN; } + + public JavaPropertiesParser(TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + public static class ParseContext extends ParserRuleContext { + public TerminalNode EOF() { return getToken(JavaPropertiesParser.EOF, 0); } + public List line() { + return getRuleContexts(LineContext.class); + } + public LineContext line(int i) { + return getRuleContext(LineContext.class,i); + } + public ParseContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_parse; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).enterParse(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).exitParse(this); + } + } + + public final ParseContext parse() throws RecognitionException { + ParseContext _localctx = new ParseContext(_ctx, getState()); + enterRule(_localctx, 0, RULE_parse); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(23); + _errHandler.sync(this); + _la = _input.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << Backslash) | (1L << Comment) | (1L << LineBreak) | (1L << Space) | (1L << IdentifierChar))) != 0)) { + { + { + setState(20); + line(); + } + } + setState(25); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(26); + match(EOF); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class LineContext extends ParserRuleContext { + public PropertyLineContext propertyLine() { + return getRuleContext(PropertyLineContext.class,0); + } + public CommentLineContext commentLine() { + return getRuleContext(CommentLineContext.class,0); + } + public EmptyLineContext emptyLine() { + return getRuleContext(EmptyLineContext.class,0); + } + public LineContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_line; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).enterLine(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).exitLine(this); + } + } + + public final LineContext line() throws RecognitionException { + LineContext _localctx = new LineContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_line); + try { + setState(31); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,1,_ctx) ) { + case 1: + enterOuterAlt(_localctx, 1); + { + setState(28); + propertyLine(); + } + break; + case 2: + enterOuterAlt(_localctx, 2); + { + setState(29); + commentLine(); + } + break; + case 3: + enterOuterAlt(_localctx, 3); + { + setState(30); + emptyLine(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class PropertyLineContext extends ParserRuleContext { + public KeyValuePairContext keyValuePair() { + return getRuleContext(KeyValuePairContext.class,0); + } + public List Space() { return getTokens(JavaPropertiesParser.Space); } + public TerminalNode Space(int i) { + return getToken(JavaPropertiesParser.Space, i); + } + public PropertyLineContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_propertyLine; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).enterPropertyLine(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).exitPropertyLine(this); + } + } + + public final PropertyLineContext propertyLine() throws RecognitionException { + PropertyLineContext _localctx = new PropertyLineContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_propertyLine); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(36); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==Space) { + { + { + setState(33); + match(Space); + } + } + setState(38); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(39); + keyValuePair(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class CommentLineContext extends ParserRuleContext { + public TerminalNode Comment() { return getToken(JavaPropertiesParser.Comment, 0); } + public TerminalNode LineBreak() { return getToken(JavaPropertiesParser.LineBreak, 0); } + public TerminalNode EOF() { return getToken(JavaPropertiesParser.EOF, 0); } + public List Space() { return getTokens(JavaPropertiesParser.Space); } + public TerminalNode Space(int i) { + return getToken(JavaPropertiesParser.Space, i); + } + public CommentLineContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_commentLine; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).enterCommentLine(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).exitCommentLine(this); + } + } + + public final CommentLineContext commentLine() throws RecognitionException { + CommentLineContext _localctx = new CommentLineContext(_ctx, getState()); + enterRule(_localctx, 6, RULE_commentLine); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(44); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==Space) { + { + { + setState(41); + match(Space); + } + } + setState(46); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(47); + match(Comment); + setState(48); + _la = _input.LA(1); + if ( !(_la==EOF || _la==LineBreak) ) { + _errHandler.recoverInline(this); + } else { + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class EmptyLineContext extends ParserRuleContext { + public TerminalNode LineBreak() { return getToken(JavaPropertiesParser.LineBreak, 0); } + public List Space() { return getTokens(JavaPropertiesParser.Space); } + public TerminalNode Space(int i) { + return getToken(JavaPropertiesParser.Space, i); + } + public EmptyLineContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_emptyLine; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).enterEmptyLine(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).exitEmptyLine(this); + } + } + + public final EmptyLineContext emptyLine() throws RecognitionException { + EmptyLineContext _localctx = new EmptyLineContext(_ctx, getState()); + enterRule(_localctx, 8, RULE_emptyLine); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(53); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==Space) { + { + { + setState(50); + match(Space); + } + } + setState(55); + _errHandler.sync(this); + _la = _input.LA(1); + } + setState(56); + match(LineBreak); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class KeyValuePairContext extends ParserRuleContext { + public KeyContext key() { + return getRuleContext(KeyContext.class,0); + } + public SeparatorAndValueContext separatorAndValue() { + return getRuleContext(SeparatorAndValueContext.class,0); + } + public TerminalNode LineBreak() { return getToken(JavaPropertiesParser.LineBreak, 0); } + public TerminalNode EOF() { return getToken(JavaPropertiesParser.EOF, 0); } + public KeyValuePairContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_keyValuePair; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).enterKeyValuePair(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).exitKeyValuePair(this); + } + } + + public final KeyValuePairContext keyValuePair() throws RecognitionException { + KeyValuePairContext _localctx = new KeyValuePairContext(_ctx, getState()); + enterRule(_localctx, 10, RULE_keyValuePair); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(58); + key(); + setState(59); + separatorAndValue(); + setState(60); + _la = _input.LA(1); + if ( !(_la==EOF || _la==LineBreak) ) { + _errHandler.recoverInline(this); + } else { + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class KeyContext extends ParserRuleContext { + public List keyChar() { + return getRuleContexts(KeyCharContext.class); + } + public KeyCharContext keyChar(int i) { + return getRuleContext(KeyCharContext.class,i); + } + public KeyContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_key; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).enterKey(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).exitKey(this); + } + } + + public final KeyContext key() throws RecognitionException { + KeyContext _localctx = new KeyContext(_ctx, getState()); + enterRule(_localctx, 12, RULE_key); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(63); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + { + setState(62); + keyChar(); + } + } + setState(65); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( _la==Backslash || _la==IdentifierChar ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class KeyCharContext extends ParserRuleContext { + public TerminalNode IdentifierChar() { return getToken(JavaPropertiesParser.IdentifierChar, 0); } + public TerminalNode Backslash() { return getToken(JavaPropertiesParser.Backslash, 0); } + public TerminalNode Colon() { return getToken(JavaPropertiesParser.Colon, 0); } + public TerminalNode Equals() { return getToken(JavaPropertiesParser.Equals, 0); } + public KeyCharContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_keyChar; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).enterKeyChar(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).exitKeyChar(this); + } + } + + public final KeyCharContext keyChar() throws RecognitionException { + KeyCharContext _localctx = new KeyCharContext(_ctx, getState()); + enterRule(_localctx, 14, RULE_keyChar); + int _la; + try { + setState(70); + switch (_input.LA(1)) { + case IdentifierChar: + enterOuterAlt(_localctx, 1); + { + setState(67); + match(IdentifierChar); + } + break; + case Backslash: + enterOuterAlt(_localctx, 2); + { + setState(68); + match(Backslash); + setState(69); + _la = _input.LA(1); + if ( !(_la==Colon || _la==Equals) ) { + _errHandler.recoverInline(this); + } else { + consume(); + } + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class SeparatorAndValueContext extends ParserRuleContext { + public TerminalNode Space() { return getToken(JavaPropertiesParser.Space, 0); } + public TerminalNode Colon() { return getToken(JavaPropertiesParser.Colon, 0); } + public TerminalNode Equals() { return getToken(JavaPropertiesParser.Equals, 0); } + public List valueChar() { + return getRuleContexts(ValueCharContext.class); + } + public ValueCharContext valueChar(int i) { + return getRuleContext(ValueCharContext.class,i); + } + public SeparatorAndValueContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_separatorAndValue; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).enterSeparatorAndValue(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).exitSeparatorAndValue(this); + } + } + + public final SeparatorAndValueContext separatorAndValue() throws RecognitionException { + SeparatorAndValueContext _localctx = new SeparatorAndValueContext(_ctx, getState()); + enterRule(_localctx, 16, RULE_separatorAndValue); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(72); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << Colon) | (1L << Equals) | (1L << Space))) != 0)) ) { + _errHandler.recoverInline(this); + } else { + consume(); + } + setState(74); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + { + setState(73); + valueChar(); + } + } + setState(76); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( (((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << Backslash) | (1L << Colon) | (1L << Equals) | (1L << Space) | (1L << IdentifierChar))) != 0) ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ValueCharContext extends ParserRuleContext { + public TerminalNode IdentifierChar() { return getToken(JavaPropertiesParser.IdentifierChar, 0); } + public TerminalNode Space() { return getToken(JavaPropertiesParser.Space, 0); } + public TerminalNode Backslash() { return getToken(JavaPropertiesParser.Backslash, 0); } + public TerminalNode LineBreak() { return getToken(JavaPropertiesParser.LineBreak, 0); } + public TerminalNode Equals() { return getToken(JavaPropertiesParser.Equals, 0); } + public TerminalNode Colon() { return getToken(JavaPropertiesParser.Colon, 0); } + public ValueCharContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_valueChar; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).enterValueChar(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof JavaPropertiesListener ) ((JavaPropertiesListener)listener).exitValueChar(this); + } + } + + public final ValueCharContext valueChar() throws RecognitionException { + ValueCharContext _localctx = new ValueCharContext(_ctx, getState()); + enterRule(_localctx, 18, RULE_valueChar); + try { + setState(84); + switch (_input.LA(1)) { + case IdentifierChar: + enterOuterAlt(_localctx, 1); + { + setState(78); + match(IdentifierChar); + } + break; + case Space: + enterOuterAlt(_localctx, 2); + { + setState(79); + match(Space); + } + break; + case Backslash: + enterOuterAlt(_localctx, 3); + { + setState(80); + match(Backslash); + setState(81); + match(LineBreak); + } + break; + case Equals: + enterOuterAlt(_localctx, 4); + { + setState(82); + match(Equals); + } + break; + case Colon: + enterOuterAlt(_localctx, 5); + { + setState(83); + match(Colon); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static final String _serializedATN = + "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3\tY\4\2\t\2\4\3\t"+ + "\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\3"+ + "\2\7\2\30\n\2\f\2\16\2\33\13\2\3\2\3\2\3\3\3\3\3\3\5\3\"\n\3\3\4\7\4%"+ + "\n\4\f\4\16\4(\13\4\3\4\3\4\3\5\7\5-\n\5\f\5\16\5\60\13\5\3\5\3\5\3\5"+ + "\3\6\7\6\66\n\6\f\6\16\69\13\6\3\6\3\6\3\7\3\7\3\7\3\7\3\b\6\bB\n\b\r"+ + "\b\16\bC\3\t\3\t\3\t\5\tI\n\t\3\n\3\n\6\nM\n\n\r\n\16\nN\3\13\3\13\3\13"+ + "\3\13\3\13\3\13\5\13W\n\13\3\13\2\2\f\2\4\6\b\n\f\16\20\22\24\2\5\3\3"+ + "\7\7\3\2\4\5\4\2\4\5\b\b[\2\31\3\2\2\2\4!\3\2\2\2\6&\3\2\2\2\b.\3\2\2"+ + "\2\n\67\3\2\2\2\f<\3\2\2\2\16A\3\2\2\2\20H\3\2\2\2\22J\3\2\2\2\24V\3\2"+ + "\2\2\26\30\5\4\3\2\27\26\3\2\2\2\30\33\3\2\2\2\31\27\3\2\2\2\31\32\3\2"+ + "\2\2\32\34\3\2\2\2\33\31\3\2\2\2\34\35\7\2\2\3\35\3\3\2\2\2\36\"\5\6\4"+ + "\2\37\"\5\b\5\2 \"\5\n\6\2!\36\3\2\2\2!\37\3\2\2\2! \3\2\2\2\"\5\3\2\2"+ + "\2#%\7\b\2\2$#\3\2\2\2%(\3\2\2\2&$\3\2\2\2&\'\3\2\2\2\')\3\2\2\2(&\3\2"+ + "\2\2)*\5\f\7\2*\7\3\2\2\2+-\7\b\2\2,+\3\2\2\2-\60\3\2\2\2.,\3\2\2\2./"+ + "\3\2\2\2/\61\3\2\2\2\60.\3\2\2\2\61\62\7\6\2\2\62\63\t\2\2\2\63\t\3\2"+ + "\2\2\64\66\7\b\2\2\65\64\3\2\2\2\669\3\2\2\2\67\65\3\2\2\2\678\3\2\2\2"+ + "8:\3\2\2\29\67\3\2\2\2:;\7\7\2\2;\13\3\2\2\2<=\5\16\b\2=>\5\22\n\2>?\t"+ + "\2\2\2?\r\3\2\2\2@B\5\20\t\2A@\3\2\2\2BC\3\2\2\2CA\3\2\2\2CD\3\2\2\2D"+ + "\17\3\2\2\2EI\7\t\2\2FG\7\3\2\2GI\t\3\2\2HE\3\2\2\2HF\3\2\2\2I\21\3\2"+ + "\2\2JL\t\4\2\2KM\5\24\13\2LK\3\2\2\2MN\3\2\2\2NL\3\2\2\2NO\3\2\2\2O\23"+ + "\3\2\2\2PW\7\t\2\2QW\7\b\2\2RS\7\3\2\2SW\7\7\2\2TW\7\5\2\2UW\7\4\2\2V"+ + "P\3\2\2\2VQ\3\2\2\2VR\3\2\2\2VT\3\2\2\2VU\3\2\2\2W\25\3\2\2\2\13\31!&"+ + ".\67CHNV"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/ParseResults.java b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/ParseResults.java new file mode 100644 index 000000000..23fbc4dd6 --- /dev/null +++ b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/ParseResults.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.properties.parser; + +import java.util.List; + +/** + * Results of parsing a text as Java Properties + * + * @author Alex Boyko + * + */ +public final class ParseResults { + + /** + * Resultant AST created based on parse tree + */ + final public PropertiesAst ast; + + /** + * Any syntax errors discovered by the parser + */ + final public List syntaxErrors; + + /** + * Any non-syntax problems discovered during parsing i.e. possible grammar problems such as ambiguity etc. + */ + final public List problems; + + public ParseResults(PropertiesAst ast, List syntaxErrors, List problems) { + this.ast = ast; + this.syntaxErrors = syntaxErrors; + this.problems = problems; + } + +} diff --git a/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/Parser.java b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/Parser.java new file mode 100644 index 000000000..375224c7b --- /dev/null +++ b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/Parser.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.properties.parser; + +/** + * Basic Parser interface + * + * @author Alex Boyko + * + */ +public interface Parser { + + /** + * Parses passed in text based on Java Properties format + * + * @param text Text to parse + * @return Results of the parsing. See {@link ParseResults} + */ + ParseResults parse(String text); + +} diff --git a/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/Problem.java b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/Problem.java new file mode 100644 index 000000000..c6f2abe1f --- /dev/null +++ b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/Problem.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.properties.parser; + +/** + * Parsing problem interface + * + * @author Alex Boyko + * + */ +public interface Problem { + + /** + * The parsing problem message + * @return The message string + */ + String getMessage(); + + /** + * Problem's code + * @return The code + */ + String getCode(); + + /** + * Problem's start index in the document + * @return Start index relative to the document + */ + int getOffset(); + + /** + * Problem's range in symbols + * @return Number of characters + */ + int getLength(); + +} diff --git a/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/ProblemCodes.java b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/ProblemCodes.java new file mode 100644 index 000000000..0c641b0d5 --- /dev/null +++ b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/ProblemCodes.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.properties.parser; + +/** + * Parsing error codes + * + * @author Alex Boyko + * + */ +public class ProblemCodes { + public static final String PROPERTIES_SYNTAX_ERROR = "PROPERTIES_SYNTAX_ERROR"; + public static final String PROPERTIES_AMBIGUITY_ERROR = "AMBIGUITY_ERROR"; + public static final String PROPERTIES_FULL_CONTEXT_ERROR = "FULL_CONTEXT_ERROR"; + public static final String PROPERTIES_CONTEXT_SENSITIVITY_ERROR = "CONTEXT_SENSITIVITY_ERROR"; +} diff --git a/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/PropertiesAst.java b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/PropertiesAst.java new file mode 100644 index 000000000..c5692e5fc --- /dev/null +++ b/vscode-extensions/commons/java-properties/src/main/java/org/springframework/ide/vscode/properties/parser/PropertiesAst.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.properties.parser; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Very basic AST for Java Properties to keep comments and key value pairs + * + * @author Alex Boyko + * + */ +public final class PropertiesAst { + + private List nodes; + + public PropertiesAst(List nodes) { + this.nodes = nodes; + } + + /** + * Retrieves all AST nodes + * @return List of AST nodes sorted by line number + */ + public List getAllNodes() { + return nodes; + } + + /** + * Retrieves AST nodes of specific type + * @param clazz Type of AST nodes + * @return List of AST nodes of specific type sorted by line number + */ + @SuppressWarnings("unchecked") + public List getNodes(Class clazz) { + List l = nodes.stream().filter(line -> { + return clazz.isAssignableFrom(line.getClass()); + }).collect(Collectors.toList()); + return (List) l; + } + + /** + * Java Properties AST node + */ + public interface Node { + + /** + * Offset index of a node in the document + * @return Offset index + */ + int getOffset(); + + /** + * Number of characters a node occupies in the document + * @return Number of characters + */ + int getLength(); + + } + + /** + * AST node for comment + */ + public interface Comment extends Node { + + } + + /** + * AST node for property key and property value pair + */ + public interface KeyValuePair extends Node { + + /** + * AST node for key + * @return Node for key + */ + Key getKey(); + + /** + * AST node for value + * @return Node for value + */ + Value getValue(); + + } + + /** + * AST node for property key + */ + public interface Key extends Node { + + /** + * Decode possibly encoded property name + * @return Decoded property name + */ + String decode(); + + } + + /** + * AST node for property value + */ + public interface Value extends Node { + + /** + * Decode possibly encoded property value + * @return Decoded property value + */ + String decode(); + + } + +} diff --git a/vscode-extensions/commons/java-properties/src/test/java/org/springframework/ide/vscode/parser/test/PropertiesAntlrParserTest.java b/vscode-extensions/commons/java-properties/src/test/java/org/springframework/ide/vscode/parser/test/PropertiesAntlrParserTest.java new file mode 100644 index 000000000..1b5d39bfd --- /dev/null +++ b/vscode-extensions/commons/java-properties/src/test/java/org/springframework/ide/vscode/parser/test/PropertiesAntlrParserTest.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, 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 + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.parser.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; +import org.springframework.ide.vscode.properties.antlr.parser.AntlrParser; +import org.springframework.ide.vscode.properties.parser.ParseResults; +import org.springframework.ide.vscode.properties.parser.Parser; +import org.springframework.ide.vscode.properties.parser.Problem; +import org.springframework.ide.vscode.properties.parser.PropertiesAst.Comment; +import org.springframework.ide.vscode.properties.parser.PropertiesAst.KeyValuePair; + +public class PropertiesAntlrParserTest { + + Parser parser = new AntlrParser(); + + private void testCommentLine(String text, String expectedComment) { + ParseResults results = parser.parse(text); + assertTrue(results.syntaxErrors.isEmpty()); + assertTrue(results.problems.isEmpty()); + assertEquals(1, results.ast.getAllNodes().size()); + + List commentLines = results.ast.getNodes(Comment.class); + assertEquals(1, commentLines.size()); + + Comment comment = commentLines.get(0); + assertEquals(expectedComment, text.substring(comment.getOffset(), comment.getOffset() + comment.getLength())); + } + + private void testPropertyLine(String text, + String expectedKey, String expectedEncodedKey, + String expectedValue, String expectedEncodedValue) { + ParseResults results = parser.parse(text); + assertTrue(results.syntaxErrors.isEmpty()); + assertTrue(results.problems.isEmpty()); + assertEquals(1, results.ast.getAllNodes().size()); + + List propertyLines = results.ast.getNodes(KeyValuePair.class); + assertEquals(1, propertyLines.size()); + + KeyValuePair line = propertyLines.get(0); + + assertNotNull(line.getKey()); + assertNotNull(line.getValue()); + + assertEquals(expectedKey, line.getKey().decode()); + assertEquals(expectedValue, line.getValue().decode()); + assertEquals(expectedEncodedKey, text.substring(line.getKey().getOffset(), line.getKey().getOffset() + line.getKey().getLength())); + assertEquals(expectedEncodedValue, text.substring(line.getValue().getOffset(), line.getValue().getOffset() + line.getValue().getLength())); + } + + @Test + public void testExclamationComment() throws Exception { + testCommentLine("! This is comment", "! This is comment"); + } + + @Test + public void testExclamationCommentWithSpaces() throws Exception { + testCommentLine(" ! This is comment = ", "! This is comment = "); + } + + @Test + public void testSharpComment() throws Exception { + testCommentLine("# This is comment", "# This is comment"); + } + + @Test + public void testSharpCommentWithSpaces() throws Exception { + testCommentLine(" # This is comment = ", "# This is comment = "); + } + + @Test + public void testLines1() throws Exception { + ParseResults results = parser.parse("# Comment\n\n \t \t \n\t\t\n"); + assertTrue(results.syntaxErrors.isEmpty()); + assertTrue(results.problems.isEmpty()); + assertEquals(1, results.ast.getAllNodes().size()); + assertEquals(1, results.ast.getNodes(Comment.class).size()); + } + + @Test + public void testLines2() throws Exception { + ParseResults results = parser.parse("\n\n \t \t \n# Comment\n\t\t\n"); + assertTrue(results.syntaxErrors.isEmpty()); + assertTrue(results.problems.isEmpty()); + assertEquals(1, results.ast.getAllNodes().size()); + assertEquals(1, results.ast.getNodes(Comment.class).size()); + } + + @Test + public void testLines3() throws Exception { + ParseResults results = parser.parse("# Comment\n\nkey = value 1 \n \t \t \n\t\t\n"); + assertTrue(results.syntaxErrors.isEmpty()); + assertTrue(results.problems.isEmpty()); + assertEquals(2, results.ast.getAllNodes().size()); + assertEquals(1, results.ast.getNodes(Comment.class).size()); + assertEquals(1, results.ast.getNodes(KeyValuePair.class).size()); + } + + @Test + public void testPropertyWithEqualsSeparator() throws Exception { + testPropertyLine("key=value", "key", "key", "value", "value"); + } + + @Test + public void testPropertyWithEqualsSeparatorAndSpaces() throws Exception { + testPropertyLine("key \t = \t \tvalue", "key", "key", "value", "value"); + } + + @Test + public void testPropertyWithColonSeparator() throws Exception { + testPropertyLine("key:value", "key", "key", "value", "value"); + } + + @Test + public void testPropertyWithColonSeparatorAndSpaces() throws Exception { + testPropertyLine("key \t : \t \tvalue", "key", "key", "value", "value"); + } + + @Test + public void testPropertyWithSpaceSeparator() throws Exception { + testPropertyLine("key value", "key", "key", "value", "value"); + } + + @Test + public void testSpacesSeparation() throws Exception { + testPropertyLine("key \t value", "key", "key", "value", "value"); + } + + @Test + public void testValueWithSpaces() throws Exception { + testPropertyLine("key=value 1 and more staff \t that is all", "key", "key", "value 1 and more staff \t that is all", "value 1 and more staff \t that is all"); + } + + @Test + public void testKeyWithLeadingSpaces() throws Exception { + testPropertyLine(" key2:value 2", "key2", "key2", "value 2", "value 2"); + } + + @Test + public void testKeyWithLeadingAndTrailingSpaces() throws Exception { + testPropertyLine(" key3 \t :value3", "key3", "key3", "value3", "value3"); + } + + @Test + public void testEncodedKeyAndValue() throws Exception { + testPropertyLine("ke\\:\\=y4=v\\\na\\\nl\\\nu\\\ne \t 4", "ke:=y4", "ke\\:\\=y4", "value \t 4", "v\\\na\\\nl\\\nu\\\ne \t 4"); + } + + @Test + public void testEqualsValue() throws Exception { + testPropertyLine("key\\=5==", "key=5", "key\\=5", "=", "="); + } + + @Test + public void testEqualsValueSeparatedWithEqualsAndSpace() throws Exception { + testPropertyLine("key7 = =", "key7", "key7", "=", "="); + } + + @Test + public void testValueWithTrailingSpaces() throws Exception { + testPropertyLine("key = value 1 ", "key", "key", "value 1 ", "value 1 "); + } + + @Test + public void testUnodeCharKeyAndValue() throws Exception { + testPropertyLine("k\u2b22ey\u2b28 = val\u2b24ue 1\u2b24 ", "k\u2b22ey\u2b28", "k\u2b22ey\u2b28", "val\u2b24ue 1\u2b24 ", "val\u2b24ue 1\u2b24 "); + } + + @Test + public void testSyntaxError() throws Exception { + String text = "abrakadabra"; + ParseResults results = parser.parse(text); + assertEquals(1, results.syntaxErrors.size()); + assertTrue(results.problems.isEmpty()); + // One property line recorded. With key and empty value + assertEquals(1, results.ast.getAllNodes().size()); + + Problem syntaxError = results.syntaxErrors.get(0); + assertEquals(text.length(), syntaxError.getOffset()); + } + + @Test + public void testMultipleSyntaxErrors() throws Exception { + String text = "abrakadabra\nkey:value\nsdcsdc"; + ParseResults results = parser.parse(text); + assertEquals(2, results.syntaxErrors.size()); + assertTrue(results.problems.isEmpty()); + // One property line recorded. With key and empty value + assertEquals(3, results.ast.getAllNodes().size()); + List lines = results.ast.getNodes(KeyValuePair.class); + assertEquals(3, lines.size()); + + // Test valid part + KeyValuePair validLine = lines.get(1); + assertNotNull(validLine.getKey()); + assertNotNull(validLine.getValue()); + assertEquals("key", text.substring(validLine.getKey().getOffset(), validLine.getKey().getOffset() + validLine.getKey().getLength())); + assertEquals("value", text.substring(validLine.getValue().getOffset(), validLine.getValue().getOffset() + validLine.getValue().getLength())); + + // Test errors + Problem syntaxError1 = results.syntaxErrors.get(0); + assertEquals(11, syntaxError1.getOffset()); + Problem syntaxError2 = results.syntaxErrors.get(1); + assertEquals(text.length(), syntaxError2.getOffset()); + } + +} diff --git a/vscode-extensions/commons/pom.xml b/vscode-extensions/commons/pom.xml index 9dff83f23..fd56e653b 100644 --- a/vscode-extensions/commons/pom.xml +++ b/vscode-extensions/commons/pom.xml @@ -14,6 +14,7 @@ language-server-test-harness yaml-commons util-commons + java-properties