SPEL syntax highlighting and validation

This commit is contained in:
aboyko
2024-06-05 10:22:43 -04:00
parent c4821359f2
commit 15f20b6f9d
44 changed files with 19126 additions and 14153 deletions

View File

@@ -23,8 +23,6 @@ public class Constants {
public static final String PREF_SCAN_JAVA_TEST_SOURCES = "boot-java.scan-java-test-sources";
public static final String PREF_VALIDATION_SPEL_EXPRESSIONS = "boot-java.validation.spel.on";
public static final String PREF_SUPPORT_SPRING_XML_CONFIGS = "boot-java.support-spring-xml-config.on";
public static final String PREF_XML_CONFIGS_SCAN_FOLDERS = "boot-java.support-spring-xml-config.scan-folders-globs";
public static final String PREF_XML_CONFIGS_HYPERLINKS = "boot-java.support-spring-xml-config.hyperlinks";

View File

@@ -183,7 +183,6 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi
Map<String, Object> bootChangeDetection = new HashMap<>();
Map<String, Object> scanTestJavaSources = new HashMap<>();
Map<String, Object> validation = new HashMap<>();
Map<String, Object> validationSpelExpressions = new HashMap<>();
Map<String, Object> javaValidation = new HashMap<>();
IPreferenceStore preferenceStore = BootLanguageServerPlugin.getDefault().getPreferenceStore();
@@ -202,8 +201,6 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi
scanTestJavaSources.put("on", preferenceStore.getBoolean(Constants.PREF_SCAN_JAVA_TEST_SOURCES));
javaValidation.put("reconcilers", preferenceStore.getBoolean(Constants.PREF_JAVA_RECONCILE));
validationSpelExpressions.put("on", preferenceStore.getBoolean(Constants.PREF_VALIDATION_SPEL_EXPRESSIONS));
validation.put("spel", validationSpelExpressions);
validation.put("java", javaValidation);
bootJavaObj.put("jpql", preferenceStore.getBoolean(Constants.PREF_JPQL));

View File

@@ -41,7 +41,6 @@ public class PrefsInitializer extends AbstractPreferenceInitializer {
preferenceStore.setDefault(Constants.PREF_XML_CONFIGS_SCAN_FOLDERS, "src/main");
preferenceStore.setDefault(Constants.PREF_CHANGE_DETECTION, false);
preferenceStore.setDefault(Constants.PREF_VALIDATION_SPEL_EXPRESSIONS, true);
preferenceStore.setDefault(Constants.PREF_SCAN_JAVA_TEST_SOURCES, false);

View File

@@ -11,4 +11,8 @@
/src/main/java/org/springframework/ide/vscode/parser/sql/MySqlParser.tokens
/src/main/java/org/springframework/ide/vscode/parser/sql/MySqlLexer.interp
/src/main/java/org/springframework/ide/vscode/parser/sql/MySqlLexer.tokens
/src/main/java/org/springframework/ide/vscode/parser/spel/SpelParser.interp
/src/main/java/org/springframework/ide/vscode/parser/spel/SpelParser.tokens
/src/main/java/org/springframework/ide/vscode/parser/spel/SpelLexer.interp
/src/main/java/org/springframework/ide/vscode/parser/spel/SpelLexer.tokens
/target

View File

@@ -9,4 +9,5 @@ cd grammars
java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar Jpql.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode/parser/jpql -package org.springframework.ide.vscode.parser.jpql
java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar Hql.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode/parser/hql -package org.springframework.ide.vscode.parser.hql
java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar MySqlLexer.g4 MySqlParser.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode/parser/sql -package org.springframework.ide.vscode.parser.sql
java -jar ${workdir}/antlr-${ANTLR_VERSION}.jar SpelLexer.g4 SpelParser.g4 -o ${workdir}/src/main/java/org/springframework/ide/vscode/parser/spel -package org.springframework.ide.vscode.parser.spel

View File

@@ -38,8 +38,6 @@ channels {
ERRORCHANNEL
}
SPEL : ('#')('{')(.)*?('}') ;
// SKIP
SPACE : [ \t\r\n]+ -> channel(HIDDEN);
@@ -1251,6 +1249,8 @@ DOUBLE_QUOTE_SYMB : '"';
REVERSE_QUOTE_SYMB : '`';
COLON_SYMB : ':';
QUESTION_SYMB : '?';
fragment QUOTE_SYMB: SINGLE_QUOTE_SYMB | DOUBLE_QUOTE_SYMB | REVERSE_QUOTE_SYMB;
// Charsets
@@ -1293,6 +1293,8 @@ HOST_IP_ADDRESS : (AT_SIGN IP_ADDRESS);
LOCAL_ID : AT_SIGN ( STRING_LITERAL | [A-Z0-9._$\u0080-\uFFFF]+);
GLOBAL_ID : AT_SIGN AT_SIGN ( [A-Z0-9._$\u0080-\uFFFF]+ | BQUOTA_STRING);
SPEL : ('#')('{')(.)*?('}') ;
// Fragments for Literal primitives
fragment CHARSET_NAME:

View File

@@ -2544,7 +2544,8 @@ predicate
// Add in ASTVisitor nullNotnull in constant
expressionAtom
: constant # constantExpressionAtom
: (QUESTION_SYMB | COLON_SYMB) SPEL # spelExpressionAtom
| constant # constantExpressionAtom
| fullColumnName # fullColumnNameExpressionAtom
| functionCall # functionCallExpressionAtom
| expressionAtom COLLATE collationName # collateExpressionAtom
@@ -2552,7 +2553,6 @@ expressionAtom
| unaryOperator expressionAtom # unaryExpressionAtom
| BINARY expressionAtom # binaryExpressionAtom
| LOCAL_ID VAR_ASSIGN expressionAtom # variableAssignExpressionAtom
| SPEL # spelExpressionAtom
| '(' expression (',' expression)* ')' # nestedExpressionAtom
| ROW '(' expression (',' expression)+ ')' # nestedRowExpressionAtom
| EXISTS '(' selectStatement ')' # existsExpressionAtom

View File

@@ -0,0 +1,193 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
lexer grammar SpelLexer;
@header {
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
}
@members {
int nesting = 0;
}
SEMICOLON: ';';
fragment NEWLINE
: '\r\n'
| [\r\n\u2028\u2029]
;
WS
: [ \t\r\n]+ -> channel(HIDDEN)
;
// ========================== S P E L ====================================================
INC: '++';
PLUS: '+';
DEC: '--';
MINUS: '-';
COLON: ':';
DOT: '.';
COMMA: ',';
STAR: '*';
DIV: '/';
MOD: '%';
LPAREN: '(';
RPAREN: ')';
LSQUARE: '[';
RSQUARE: ']';
HASH: '#';
BEAN_REF: '@';
SELECT_FIRST: '^[';
POWER: '^';
NE: '!=';
PROJECT: '![';
NOT: '!';
EQ: '==';
ASSIGN: '=';
SYMBOLIC_AND: '&&';
FACTORY_BEAN_REF: '&';
SYMBOLIC_OR: '||';
SELECT: '?[';
ELVIS: '?:';
SAFE_NAVI: '?.';
QMARK: '?';
SELECT_LAST: '$[';
GE: '>=';
GT: '>';
LE: '<=';
LT: '<';
// Special treatment to support template literals
LCURLY: '{' { nesting++; } -> pushMode(DEFAULT_MODE);
RCURLY: '}' {
if (nesting > 0) {
nesting--;
}
} -> popMode;
BACKTICK
: '`' -> pushMode(IN_TEMPLATE_STRING)
;
OR: 'or';
AND: 'and';
TRUE: 'true';
FALSE: 'false';
NEW: 'new';
NULL: 'null';
T: 'T';
MATCHES: 'matches';
GT_KEYWORD: 'gt';
GE_KEYWORD: 'ge';
LE_KEYWORD: 'le';
LT_KEYWORD: 'lt';
EQ_KEYWORD: 'eq';
NE_KEYWORD: 'ne';
// IDENTIFIER appearing AFTER tokens like OR and AND make those lex as special tokens,
// like SpEL's lexIdentifier() does
IDENTIFIER
: (ALPHABETIC | '_') (ALPHABETIC | DIGIT | '_' | '$')*
;
NUMERIC_LITERAL
: REAL_LITERAL
| INTEGER_LITERAL
;
REAL_LITERAL
: '.' DECIMAL_DIGIT+ EXPONENT_PART? REAL_TYPE_SUFFIX?
| DECIMAL_DIGIT+ '.' DECIMAL_DIGIT+ EXPONENT_PART? REAL_TYPE_SUFFIX?
| DECIMAL_DIGIT+ EXPONENT_PART REAL_TYPE_SUFFIX?
| DECIMAL_DIGIT+ REAL_TYPE_SUFFIX
;
INTEGER_LITERAL
: DECIMAL_DIGIT+ INTEGER_TYPE_SUFFIX?
;
STRING_LITERAL
: SINGLE_QUOTED_STRING
| DOUBLE_QUOTED_STRING
;
SINGLE_QUOTED_STRING
: '\'' ( '\'\'' | ~['\n] )* '\''
;
DOUBLE_QUOTED_STRING
: '"' ( '""' | ~["\n] )* '"'
;
fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' );
fragment HEX_DIGIT : [0-9A-Fa-f];
fragment DECIMAL_DIGIT: [0-9];
fragment EXPONENT_PART
: 'e' (SIGN)* (DECIMAL_DIGIT)+
| 'E' (SIGN)* (DECIMAL_DIGIT)+
;
fragment SIGN : '+' | '-' ;
fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd';
fragment ALPHABETIC
// See Character.isLetter()
// and https://github.com/antlr/antlr4/blob/master/doc/lexer-rules.md#lexer-rule-elements
// and https://github.com/spring-projects/spring-framework/commit/c8c8f5722bc58452894742534954c0935653771f
: [\p{Lu}] // UPPERCASE_LETTER
| [\p{Ll}] // LOWERCASE_LETTER
| [\p{Lt}] // TITLECASE_LETTER
| [\p{Lm}] // MODIFIER_LETTER
| [\p{Lo}] // OTHER_LETTER
;
fragment DIGIT
: [0-9]
;
mode IN_TEMPLATE_STRING;
ESCAPED_BACKTICK
: '``'
;
SPEL_IN_TEMPLATE_STRING_OPEN: '#{' { nesting++; } -> pushMode(DEFAULT_MODE);
TEMPLATE_TEXT
: ~[`\n]
;
BACKTICK_IN_TEMPLATE
: '`' -> type(BACKTICK), popMode
;

View File

@@ -0,0 +1,171 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
parser grammar SpelParser;
@header {
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
}
options {
tokenVocab = SpelLexer;
}
script
: spelExpr EOF
;
spelExpr
: (PLUS | MINUS | NOT | INC | DEC) spelExpr // ~unaryspelExpr
| spelExpr (INC | DEC) // ~incspelExpr
| spelExpr POWER spelExpr // ~powerExpr
| spelExpr (STAR | DIV | MOD) spelExpr // ~productspelExpr
| spelExpr (PLUS | MINUS) spelExpr // ~sumspelExpr
| spelExpr (GT | LT | LE | GE | EQ | NE | GT_KEYWORD | LT_KEYWORD | LE_KEYWORD | GE_KEYWORD | EQ_KEYWORD | NE_KEYWORD) spelExpr // ~relationalspelExpr
| spelExpr (AND | SYMBOLIC_AND) spelExpr // ~logicalAndspelExpr
| spelExpr (OR | SYMBOLIC_OR) spelExpr // ~logicalOrspelExpr
| spelExpr MATCHES spelExpr // ~regexExpr
// spelExpr
| spelExpr ASSIGN spelExpr
| spelExpr ELVIS spelExpr
| spelExpr QMARK spelExpr COLON spelExpr
// primaryspelExpr
| startNode node*
;
node
: dottedNode
| nonDottedNode
;
nonDottedNode
: (LSQUARE spelExpr RSQUARE)
;
dottedNode
: (DOT | SAFE_NAVI) methodOrProperty
| functionOrVar
| projection
| selection
;
functionOrVar
: HASH IDENTIFIER
| HASH IDENTIFIER methodArgs
;
methodArgs
: LPAREN args RPAREN
;
args
: spelExpr? (COMMA spelExpr)*
;
methodOrProperty
: IDENTIFIER
| IDENTIFIER methodArgs
;
projection
: PROJECT spelExpr RSQUARE
;
selection
: (SELECT | SELECT_FIRST | SELECT_LAST) spelExpr RSQUARE
;
startNode
: literal
| parenspelExpr
| typeReference
| nullReference
| constructorReference
| methodOrProperty
| functionOrVar
| beanReference
| projection
| selection
| inlineListOrMap
;
literal
: NUMERIC_LITERAL
| STRING_LITERAL
| TRUE
| FALSE
;
parenspelExpr
: LPAREN spelExpr RPAREN
;
typeReference
: T LPAREN possiblyQualifiedId (LSQUARE RSQUARE)* RPAREN
;
possiblyQualifiedId
: IDENTIFIER (DOT IDENTIFIER)*
;
nullReference
: NULL
;
constructorReference
: NEW possiblyQualifiedId (LSQUARE spelExpr? RSQUARE)+ inlineListOrMap?
| NEW possiblyQualifiedId constructorArgs
;
constructorArgs
: LPAREN args RPAREN
;
inlineListOrMap
: LCURLY RCURLY
| LCURLY COLON RCURLY
| LCURLY listBindings RCURLY
| LCURLY mapBindings RCURLY
;
listBindings
: bindings+=listBinding (COMMA bindings+=listBinding)*
;
listBinding
: spelExpr
;
mapBindings
: bindings+=mapBinding (COMMA bindings+=mapBinding)*
;
mapBinding
: key=spelExpr COLON value=spelExpr
;
beanReference
: (BEAN_REF | FACTORY_BEAN_REF) (IDENTIFIER | STRING_LITERAL)
;

View File

@@ -0,0 +1,672 @@
// Generated from SpelLexer.g4 by ANTLR 4.13.1
package org.springframework.ide.vscode.parser.spel;
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
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", "CheckReturnValue", "this-escape"})
public class SpelLexer extends Lexer {
static { RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION); }
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
public static final int
SEMICOLON=1, WS=2, INC=3, PLUS=4, DEC=5, MINUS=6, COLON=7, DOT=8, COMMA=9,
STAR=10, DIV=11, MOD=12, LPAREN=13, RPAREN=14, LSQUARE=15, RSQUARE=16,
HASH=17, BEAN_REF=18, SELECT_FIRST=19, POWER=20, NE=21, PROJECT=22, NOT=23,
EQ=24, ASSIGN=25, SYMBOLIC_AND=26, FACTORY_BEAN_REF=27, SYMBOLIC_OR=28,
SELECT=29, ELVIS=30, SAFE_NAVI=31, QMARK=32, SELECT_LAST=33, GE=34, GT=35,
LE=36, LT=37, LCURLY=38, RCURLY=39, BACKTICK=40, OR=41, AND=42, TRUE=43,
FALSE=44, NEW=45, NULL=46, T=47, MATCHES=48, GT_KEYWORD=49, GE_KEYWORD=50,
LE_KEYWORD=51, LT_KEYWORD=52, EQ_KEYWORD=53, NE_KEYWORD=54, IDENTIFIER=55,
NUMERIC_LITERAL=56, REAL_LITERAL=57, INTEGER_LITERAL=58, STRING_LITERAL=59,
SINGLE_QUOTED_STRING=60, DOUBLE_QUOTED_STRING=61, ESCAPED_BACKTICK=62,
SPEL_IN_TEMPLATE_STRING_OPEN=63, TEMPLATE_TEXT=64;
public static final int
IN_TEMPLATE_STRING=1;
public static String[] channelNames = {
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
};
public static String[] modeNames = {
"DEFAULT_MODE", "IN_TEMPLATE_STRING"
};
private static String[] makeRuleNames() {
return new String[] {
"SEMICOLON", "NEWLINE", "WS", "INC", "PLUS", "DEC", "MINUS", "COLON",
"DOT", "COMMA", "STAR", "DIV", "MOD", "LPAREN", "RPAREN", "LSQUARE",
"RSQUARE", "HASH", "BEAN_REF", "SELECT_FIRST", "POWER", "NE", "PROJECT",
"NOT", "EQ", "ASSIGN", "SYMBOLIC_AND", "FACTORY_BEAN_REF", "SYMBOLIC_OR",
"SELECT", "ELVIS", "SAFE_NAVI", "QMARK", "SELECT_LAST", "GE", "GT", "LE",
"LT", "LCURLY", "RCURLY", "BACKTICK", "OR", "AND", "TRUE", "FALSE", "NEW",
"NULL", "T", "MATCHES", "GT_KEYWORD", "GE_KEYWORD", "LE_KEYWORD", "LT_KEYWORD",
"EQ_KEYWORD", "NE_KEYWORD", "IDENTIFIER", "NUMERIC_LITERAL", "REAL_LITERAL",
"INTEGER_LITERAL", "STRING_LITERAL", "SINGLE_QUOTED_STRING", "DOUBLE_QUOTED_STRING",
"INTEGER_TYPE_SUFFIX", "HEX_DIGIT", "DECIMAL_DIGIT", "EXPONENT_PART",
"SIGN", "REAL_TYPE_SUFFIX", "ALPHABETIC", "DIGIT", "ESCAPED_BACKTICK",
"SPEL_IN_TEMPLATE_STRING_OPEN", "TEMPLATE_TEXT", "BACKTICK_IN_TEMPLATE"
};
}
public static final String[] ruleNames = makeRuleNames();
private static String[] makeLiteralNames() {
return new String[] {
null, "';'", null, "'++'", "'+'", "'--'", "'-'", "':'", "'.'", "','",
"'*'", "'/'", "'%'", "'('", "')'", "'['", "']'", "'#'", "'@'", "'^['",
"'^'", "'!='", "'!['", "'!'", "'=='", "'='", "'&&'", "'&'", "'||'", "'?['",
"'?:'", "'?.'", "'?'", "'$['", "'>='", "'>'", "'<='", "'<'", null, null,
null, "'or'", "'and'", "'true'", "'false'", "'new'", "'null'", "'T'",
"'matches'", "'gt'", "'ge'", "'le'", "'lt'", "'eq'", "'ne'", null, null,
null, null, null, null, null, "'``'"
};
}
private static final String[] _LITERAL_NAMES = makeLiteralNames();
private static String[] makeSymbolicNames() {
return new String[] {
null, "SEMICOLON", "WS", "INC", "PLUS", "DEC", "MINUS", "COLON", "DOT",
"COMMA", "STAR", "DIV", "MOD", "LPAREN", "RPAREN", "LSQUARE", "RSQUARE",
"HASH", "BEAN_REF", "SELECT_FIRST", "POWER", "NE", "PROJECT", "NOT",
"EQ", "ASSIGN", "SYMBOLIC_AND", "FACTORY_BEAN_REF", "SYMBOLIC_OR", "SELECT",
"ELVIS", "SAFE_NAVI", "QMARK", "SELECT_LAST", "GE", "GT", "LE", "LT",
"LCURLY", "RCURLY", "BACKTICK", "OR", "AND", "TRUE", "FALSE", "NEW",
"NULL", "T", "MATCHES", "GT_KEYWORD", "GE_KEYWORD", "LE_KEYWORD", "LT_KEYWORD",
"EQ_KEYWORD", "NE_KEYWORD", "IDENTIFIER", "NUMERIC_LITERAL", "REAL_LITERAL",
"INTEGER_LITERAL", "STRING_LITERAL", "SINGLE_QUOTED_STRING", "DOUBLE_QUOTED_STRING",
"ESCAPED_BACKTICK", "SPEL_IN_TEMPLATE_STRING_OPEN", "TEMPLATE_TEXT"
};
}
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
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] = "<INVALID>";
}
}
}
@Override
@Deprecated
public String[] getTokenNames() {
return tokenNames;
}
@Override
public Vocabulary getVocabulary() {
return VOCABULARY;
}
int nesting = 0;
public SpelLexer(CharStream input) {
super(input);
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
}
@Override
public String getGrammarFileName() { return "SpelLexer.g4"; }
@Override
public String[] getRuleNames() { return ruleNames; }
@Override
public String getSerializedATN() { return _serializedATN; }
@Override
public String[] getChannelNames() { return channelNames; }
@Override
public String[] getModeNames() { return modeNames; }
@Override
public ATN getATN() { return _ATN; }
@Override
public void action(RuleContext _localctx, int ruleIndex, int actionIndex) {
switch (ruleIndex) {
case 38:
LCURLY_action((RuleContext)_localctx, actionIndex);
break;
case 39:
RCURLY_action((RuleContext)_localctx, actionIndex);
break;
case 71:
SPEL_IN_TEMPLATE_STRING_OPEN_action((RuleContext)_localctx, actionIndex);
break;
}
}
private void LCURLY_action(RuleContext _localctx, int actionIndex) {
switch (actionIndex) {
case 0:
nesting++;
break;
}
}
private void RCURLY_action(RuleContext _localctx, int actionIndex) {
switch (actionIndex) {
case 1:
if (nesting > 0) {
nesting--;
}
break;
}
}
private void SPEL_IN_TEMPLATE_STRING_OPEN_action(RuleContext _localctx, int actionIndex) {
switch (actionIndex) {
case 2:
nesting++;
break;
}
}
public static final String _serializedATN =
"\u0004\u0000@\u01d8\u0006\uffff\uffff\u0006\uffff\uffff\u0002\u0000\u0007"+
"\u0000\u0002\u0001\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007"+
"\u0003\u0002\u0004\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007"+
"\u0006\u0002\u0007\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n"+
"\u0007\n\u0002\u000b\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002"+
"\u000e\u0007\u000e\u0002\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002"+
"\u0011\u0007\u0011\u0002\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002"+
"\u0014\u0007\u0014\u0002\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002"+
"\u0017\u0007\u0017\u0002\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002"+
"\u001a\u0007\u001a\u0002\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002"+
"\u001d\u0007\u001d\u0002\u001e\u0007\u001e\u0002\u001f\u0007\u001f\u0002"+
" \u0007 \u0002!\u0007!\u0002\"\u0007\"\u0002#\u0007#\u0002$\u0007$\u0002"+
"%\u0007%\u0002&\u0007&\u0002\'\u0007\'\u0002(\u0007(\u0002)\u0007)\u0002"+
"*\u0007*\u0002+\u0007+\u0002,\u0007,\u0002-\u0007-\u0002.\u0007.\u0002"+
"/\u0007/\u00020\u00070\u00021\u00071\u00022\u00072\u00023\u00073\u0002"+
"4\u00074\u00025\u00075\u00026\u00076\u00027\u00077\u00028\u00078\u0002"+
"9\u00079\u0002:\u0007:\u0002;\u0007;\u0002<\u0007<\u0002=\u0007=\u0002"+
">\u0007>\u0002?\u0007?\u0002@\u0007@\u0002A\u0007A\u0002B\u0007B\u0002"+
"C\u0007C\u0002D\u0007D\u0002E\u0007E\u0002F\u0007F\u0002G\u0007G\u0002"+
"H\u0007H\u0002I\u0007I\u0001\u0000\u0001\u0000\u0001\u0001\u0001\u0001"+
"\u0001\u0001\u0003\u0001\u009c\b\u0001\u0001\u0002\u0004\u0002\u009f\b"+
"\u0002\u000b\u0002\f\u0002\u00a0\u0001\u0002\u0001\u0002\u0001\u0003\u0001"+
"\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005\u0001"+
"\u0005\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001\b\u0001\b"+
"\u0001\t\u0001\t\u0001\n\u0001\n\u0001\u000b\u0001\u000b\u0001\f\u0001"+
"\f\u0001\r\u0001\r\u0001\u000e\u0001\u000e\u0001\u000f\u0001\u000f\u0001"+
"\u0010\u0001\u0010\u0001\u0011\u0001\u0011\u0001\u0012\u0001\u0012\u0001"+
"\u0013\u0001\u0013\u0001\u0013\u0001\u0014\u0001\u0014\u0001\u0015\u0001"+
"\u0015\u0001\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0017\u0001"+
"\u0017\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0019\u0001\u0019\u0001"+
"\u001a\u0001\u001a\u0001\u001a\u0001\u001b\u0001\u001b\u0001\u001c\u0001"+
"\u001c\u0001\u001c\u0001\u001d\u0001\u001d\u0001\u001d\u0001\u001e\u0001"+
"\u001e\u0001\u001e\u0001\u001f\u0001\u001f\u0001\u001f\u0001 \u0001 \u0001"+
"!\u0001!\u0001!\u0001\"\u0001\"\u0001\"\u0001#\u0001#\u0001$\u0001$\u0001"+
"$\u0001%\u0001%\u0001&\u0001&\u0001&\u0001&\u0001&\u0001\'\u0001\'\u0001"+
"\'\u0001\'\u0001\'\u0001(\u0001(\u0001(\u0001(\u0001)\u0001)\u0001)\u0001"+
"*\u0001*\u0001*\u0001*\u0001+\u0001+\u0001+\u0001+\u0001+\u0001,\u0001"+
",\u0001,\u0001,\u0001,\u0001,\u0001-\u0001-\u0001-\u0001-\u0001.\u0001"+
".\u0001.\u0001.\u0001.\u0001/\u0001/\u00010\u00010\u00010\u00010\u0001"+
"0\u00010\u00010\u00010\u00011\u00011\u00011\u00012\u00012\u00012\u0001"+
"3\u00013\u00013\u00014\u00014\u00014\u00015\u00015\u00015\u00016\u0001"+
"6\u00016\u00017\u00017\u00037\u0140\b7\u00017\u00017\u00017\u00057\u0145"+
"\b7\n7\f7\u0148\t7\u00018\u00018\u00038\u014c\b8\u00019\u00019\u00049"+
"\u0150\b9\u000b9\f9\u0151\u00019\u00039\u0155\b9\u00019\u00039\u0158\b"+
"9\u00019\u00049\u015b\b9\u000b9\f9\u015c\u00019\u00019\u00049\u0161\b"+
"9\u000b9\f9\u0162\u00019\u00039\u0166\b9\u00019\u00039\u0169\b9\u0001"+
"9\u00049\u016c\b9\u000b9\f9\u016d\u00019\u00019\u00039\u0172\b9\u0001"+
"9\u00049\u0175\b9\u000b9\f9\u0176\u00019\u00019\u00039\u017b\b9\u0001"+
":\u0004:\u017e\b:\u000b:\f:\u017f\u0001:\u0003:\u0183\b:\u0001;\u0001"+
";\u0003;\u0187\b;\u0001<\u0001<\u0001<\u0001<\u0005<\u018d\b<\n<\f<\u0190"+
"\t<\u0001<\u0001<\u0001=\u0001=\u0001=\u0001=\u0005=\u0198\b=\n=\f=\u019b"+
"\t=\u0001=\u0001=\u0001>\u0001>\u0001?\u0001?\u0001@\u0001@\u0001A\u0001"+
"A\u0005A\u01a7\bA\nA\fA\u01aa\tA\u0001A\u0004A\u01ad\bA\u000bA\fA\u01ae"+
"\u0001A\u0001A\u0005A\u01b3\bA\nA\fA\u01b6\tA\u0001A\u0004A\u01b9\bA\u000b"+
"A\fA\u01ba\u0003A\u01bd\bA\u0001B\u0001B\u0001C\u0001C\u0001D\u0003D\u01c4"+
"\bD\u0001E\u0001E\u0001F\u0001F\u0001F\u0001G\u0001G\u0001G\u0001G\u0001"+
"G\u0001G\u0001G\u0001H\u0001H\u0001I\u0001I\u0001I\u0001I\u0001I\u0000"+
"\u0000J\u0002\u0001\u0004\u0000\u0006\u0002\b\u0003\n\u0004\f\u0005\u000e"+
"\u0006\u0010\u0007\u0012\b\u0014\t\u0016\n\u0018\u000b\u001a\f\u001c\r"+
"\u001e\u000e \u000f\"\u0010$\u0011&\u0012(\u0013*\u0014,\u0015.\u0016"+
"0\u00172\u00184\u00196\u001a8\u001b:\u001c<\u001d>\u001e@\u001fB D!F\""+
"H#J$L%N&P\'R(T)V*X+Z,\\-^.`/b0d1f2h3j4l5n6p7r8t9v:x;z<|=~\u0000\u0080"+
"\u0000\u0082\u0000\u0084\u0000\u0086\u0000\u0088\u0000\u008a\u0000\u008c"+
"\u0000\u008e>\u0090?\u0092@\u0094\u0000\u0002\u0000\u0001\f\u0003\u0000"+
"\n\n\r\r\u2028\u2029\u0003\u0000\t\n\r\r \u0002\u0000$$__\u0002\u0000"+
"\n\n\'\'\u0002\u0000\n\n\"\"\u0002\u0000LLll\u0003\u000009AFaf\u0001\u0000"+
"09\u0002\u0000++--\u0004\u0000DDFFddff\u0293\u0000AZaz\u00aa\u00aa\u00b5"+
"\u00b5\u00ba\u00ba\u00c0\u00d6\u00d8\u00f6\u00f8\u02c1\u02c6\u02d1\u02e0"+
"\u02e4\u02ec\u02ec\u02ee\u02ee\u0370\u0374\u0376\u0377\u037a\u037d\u037f"+
"\u037f\u0386\u0386\u0388\u038a\u038c\u038c\u038e\u03a1\u03a3\u03f5\u03f7"+
"\u0481\u048a\u052f\u0531\u0556\u0559\u0559\u0560\u0588\u05d0\u05ea\u05ef"+
"\u05f2\u0620\u064a\u066e\u066f\u0671\u06d3\u06d5\u06d5\u06e5\u06e6\u06ee"+
"\u06ef\u06fa\u06fc\u06ff\u06ff\u0710\u0710\u0712\u072f\u074d\u07a5\u07b1"+
"\u07b1\u07ca\u07ea\u07f4\u07f5\u07fa\u07fa\u0800\u0815\u081a\u081a\u0824"+
"\u0824\u0828\u0828\u0840\u0858\u0860\u086a\u0870\u0887\u0889\u088e\u08a0"+
"\u08c9\u0904\u0939\u093d\u093d\u0950\u0950\u0958\u0961\u0971\u0980\u0985"+
"\u098c\u098f\u0990\u0993\u09a8\u09aa\u09b0\u09b2\u09b2\u09b6\u09b9\u09bd"+
"\u09bd\u09ce\u09ce\u09dc\u09dd\u09df\u09e1\u09f0\u09f1\u09fc\u09fc\u0a05"+
"\u0a0a\u0a0f\u0a10\u0a13\u0a28\u0a2a\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38"+
"\u0a39\u0a59\u0a5c\u0a5e\u0a5e\u0a72\u0a74\u0a85\u0a8d\u0a8f\u0a91\u0a93"+
"\u0aa8\u0aaa\u0ab0\u0ab2\u0ab3\u0ab5\u0ab9\u0abd\u0abd\u0ad0\u0ad0\u0ae0"+
"\u0ae1\u0af9\u0af9\u0b05\u0b0c\u0b0f\u0b10\u0b13\u0b28\u0b2a\u0b30\u0b32"+
"\u0b33\u0b35\u0b39\u0b3d\u0b3d\u0b5c\u0b5d\u0b5f\u0b61\u0b71\u0b71\u0b83"+
"\u0b83\u0b85\u0b8a\u0b8e\u0b90\u0b92\u0b95\u0b99\u0b9a\u0b9c\u0b9c\u0b9e"+
"\u0b9f\u0ba3\u0ba4\u0ba8\u0baa\u0bae\u0bb9\u0bd0\u0bd0\u0c05\u0c0c\u0c0e"+
"\u0c10\u0c12\u0c28\u0c2a\u0c39\u0c3d\u0c3d\u0c58\u0c5a\u0c5d\u0c5d\u0c60"+
"\u0c61\u0c80\u0c80\u0c85\u0c8c\u0c8e\u0c90\u0c92\u0ca8\u0caa\u0cb3\u0cb5"+
"\u0cb9\u0cbd\u0cbd\u0cdd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04\u0d0c\u0d0e"+
"\u0d10\u0d12\u0d3a\u0d3d\u0d3d\u0d4e\u0d4e\u0d54\u0d56\u0d5f\u0d61\u0d7a"+
"\u0d7f\u0d85\u0d96\u0d9a\u0db1\u0db3\u0dbb\u0dbd\u0dbd\u0dc0\u0dc6\u0e01"+
"\u0e30\u0e32\u0e33\u0e40\u0e46\u0e81\u0e82\u0e84\u0e84\u0e86\u0e8a\u0e8c"+
"\u0ea3\u0ea5\u0ea5\u0ea7\u0eb0\u0eb2\u0eb3\u0ebd\u0ebd\u0ec0\u0ec4\u0ec6"+
"\u0ec6\u0edc\u0edf\u0f00\u0f00\u0f40\u0f47\u0f49\u0f6c\u0f88\u0f8c\u1000"+
"\u102a\u103f\u103f\u1050\u1055\u105a\u105d\u1061\u1061\u1065\u1066\u106e"+
"\u1070\u1075\u1081\u108e\u108e\u10a0\u10c5\u10c7\u10c7\u10cd\u10cd\u10d0"+
"\u10fa\u10fc\u1248\u124a\u124d\u1250\u1256\u1258\u1258\u125a\u125d\u1260"+
"\u1288\u128a\u128d\u1290\u12b0\u12b2\u12b5\u12b8\u12be\u12c0\u12c0\u12c2"+
"\u12c5\u12c8\u12d6\u12d8\u1310\u1312\u1315\u1318\u135a\u1380\u138f\u13a0"+
"\u13f5\u13f8\u13fd\u1401\u166c\u166f\u167f\u1681\u169a\u16a0\u16ea\u16f1"+
"\u16f8\u1700\u1711\u171f\u1731\u1740\u1751\u1760\u176c\u176e\u1770\u1780"+
"\u17b3\u17d7\u17d7\u17dc\u17dc\u1820\u1878\u1880\u1884\u1887\u18a8\u18aa"+
"\u18aa\u18b0\u18f5\u1900\u191e\u1950\u196d\u1970\u1974\u1980\u19ab\u19b0"+
"\u19c9\u1a00\u1a16\u1a20\u1a54\u1aa7\u1aa7\u1b05\u1b33\u1b45\u1b4c\u1b83"+
"\u1ba0\u1bae\u1baf\u1bba\u1be5\u1c00\u1c23\u1c4d\u1c4f\u1c5a\u1c7d\u1c80"+
"\u1c88\u1c90\u1cba\u1cbd\u1cbf\u1ce9\u1cec\u1cee\u1cf3\u1cf5\u1cf6\u1cfa"+
"\u1cfa\u1d00\u1dbf\u1e00\u1f15\u1f18\u1f1d\u1f20\u1f45\u1f48\u1f4d\u1f50"+
"\u1f57\u1f59\u1f59\u1f5b\u1f5b\u1f5d\u1f5d\u1f5f\u1f7d\u1f80\u1fb4\u1fb6"+
"\u1fbc\u1fbe\u1fbe\u1fc2\u1fc4\u1fc6\u1fcc\u1fd0\u1fd3\u1fd6\u1fdb\u1fe0"+
"\u1fec\u1ff2\u1ff4\u1ff6\u1ffc\u2071\u2071\u207f\u207f\u2090\u209c\u2102"+
"\u2102\u2107\u2107\u210a\u2113\u2115\u2115\u2119\u211d\u2124\u2124\u2126"+
"\u2126\u2128\u2128\u212a\u212d\u212f\u2139\u213c\u213f\u2145\u2149\u214e"+
"\u214e\u2183\u2184\u2c00\u2ce4\u2ceb\u2cee\u2cf2\u2cf3\u2d00\u2d25\u2d27"+
"\u2d27\u2d2d\u2d2d\u2d30\u2d67\u2d6f\u2d6f\u2d80\u2d96\u2da0\u2da6\u2da8"+
"\u2dae\u2db0\u2db6\u2db8\u2dbe\u2dc0\u2dc6\u2dc8\u2dce\u2dd0\u2dd6\u2dd8"+
"\u2dde\u2e2f\u2e2f\u3005\u3006\u3031\u3035\u303b\u303c\u3041\u3096\u309d"+
"\u309f\u30a1\u30fa\u30fc\u30ff\u3105\u312f\u3131\u318e\u31a0\u31bf\u31f0"+
"\u31ff\u3400\u4dbf\u4e00\u8000\ua48c\u8000\ua4d0\u8000\ua4fd\u8000\ua500"+
"\u8000\ua60c\u8000\ua610\u8000\ua61f\u8000\ua62a\u8000\ua62b\u8000\ua640"+
"\u8000\ua66e\u8000\ua67f\u8000\ua69d\u8000\ua6a0\u8000\ua6e5\u8000\ua717"+
"\u8000\ua71f\u8000\ua722\u8000\ua788\u8000\ua78b\u8000\ua7ca\u8000\ua7d0"+
"\u8000\ua7d1\u8000\ua7d3\u8000\ua7d3\u8000\ua7d5\u8000\ua7d9\u8000\ua7f2"+
"\u8000\ua801\u8000\ua803\u8000\ua805\u8000\ua807\u8000\ua80a\u8000\ua80c"+
"\u8000\ua822\u8000\ua840\u8000\ua873\u8000\ua882\u8000\ua8b3\u8000\ua8f2"+
"\u8000\ua8f7\u8000\ua8fb\u8000\ua8fb\u8000\ua8fd\u8000\ua8fe\u8000\ua90a"+
"\u8000\ua925\u8000\ua930\u8000\ua946\u8000\ua960\u8000\ua97c\u8000\ua984"+
"\u8000\ua9b2\u8000\ua9cf\u8000\ua9cf\u8000\ua9e0\u8000\ua9e4\u8000\ua9e6"+
"\u8000\ua9ef\u8000\ua9fa\u8000\ua9fe\u8000\uaa00\u8000\uaa28\u8000\uaa40"+
"\u8000\uaa42\u8000\uaa44\u8000\uaa4b\u8000\uaa60\u8000\uaa76\u8000\uaa7a"+
"\u8000\uaa7a\u8000\uaa7e\u8000\uaaaf\u8000\uaab1\u8000\uaab1\u8000\uaab5"+
"\u8000\uaab6\u8000\uaab9\u8000\uaabd\u8000\uaac0\u8000\uaac0\u8000\uaac2"+
"\u8000\uaac2\u8000\uaadb\u8000\uaadd\u8000\uaae0\u8000\uaaea\u8000\uaaf2"+
"\u8000\uaaf4\u8000\uab01\u8000\uab06\u8000\uab09\u8000\uab0e\u8000\uab11"+
"\u8000\uab16\u8000\uab20\u8000\uab26\u8000\uab28\u8000\uab2e\u8000\uab30"+
"\u8000\uab5a\u8000\uab5c\u8000\uab69\u8000\uab70\u8000\uabe2\u8000\uac00"+
"\u8000\ud7a3\u8000\ud7b0\u8000\ud7c6\u8000\ud7cb\u8000\ud7fb\u8000\uf900"+
"\u8000\ufa6d\u8000\ufa70\u8000\ufad9\u8000\ufb00\u8000\ufb06\u8000\ufb13"+
"\u8000\ufb17\u8000\ufb1d\u8000\ufb1d\u8000\ufb1f\u8000\ufb28\u8000\ufb2a"+
"\u8000\ufb36\u8000\ufb38\u8000\ufb3c\u8000\ufb3e\u8000\ufb3e\u8000\ufb40"+
"\u8000\ufb41\u8000\ufb43\u8000\ufb44\u8000\ufb46\u8000\ufbb1\u8000\ufbd3"+
"\u8000\ufd3d\u8000\ufd50\u8000\ufd8f\u8000\ufd92\u8000\ufdc7\u8000\ufdf0"+
"\u8000\ufdfb\u8000\ufe70\u8000\ufe74\u8000\ufe76\u8000\ufefc\u8000\uff21"+
"\u8000\uff3a\u8000\uff41\u8000\uff5a\u8000\uff66\u8000\uffbe\u8000\uffc2"+
"\u8000\uffc7\u8000\uffca\u8000\uffcf\u8000\uffd2\u8000\uffd7\u8000\uffda"+
"\u8000\uffdc\u8001\u0000\u8001\u000b\u8001\r\u8001&\u8001(\u8001:\u8001"+
"<\u8001=\u8001?\u8001M\u8001P\u8001]\u8001\u0080\u8001\u00fa\u8001\u0280"+
"\u8001\u029c\u8001\u02a0\u8001\u02d0\u8001\u0300\u8001\u031f\u8001\u032d"+
"\u8001\u0340\u8001\u0342\u8001\u0349\u8001\u0350\u8001\u0375\u8001\u0380"+
"\u8001\u039d\u8001\u03a0\u8001\u03c3\u8001\u03c8\u8001\u03cf\u8001\u0400"+
"\u8001\u049d\u8001\u04b0\u8001\u04d3\u8001\u04d8\u8001\u04fb\u8001\u0500"+
"\u8001\u0527\u8001\u0530\u8001\u0563\u8001\u0570\u8001\u057a\u8001\u057c"+
"\u8001\u058a\u8001\u058c\u8001\u0592\u8001\u0594\u8001\u0595\u8001\u0597"+
"\u8001\u05a1\u8001\u05a3\u8001\u05b1\u8001\u05b3\u8001\u05b9\u8001\u05bb"+
"\u8001\u05bc\u8001\u0600\u8001\u0736\u8001\u0740\u8001\u0755\u8001\u0760"+
"\u8001\u0767\u8001\u0780\u8001\u0785\u8001\u0787\u8001\u07b0\u8001\u07b2"+
"\u8001\u07ba\u8001\u0800\u8001\u0805\u8001\u0808\u8001\u0808\u8001\u080a"+
"\u8001\u0835\u8001\u0837\u8001\u0838\u8001\u083c\u8001\u083c\u8001\u083f"+
"\u8001\u0855\u8001\u0860\u8001\u0876\u8001\u0880\u8001\u089e\u8001\u08e0"+
"\u8001\u08f2\u8001\u08f4\u8001\u08f5\u8001\u0900\u8001\u0915\u8001\u0920"+
"\u8001\u0939\u8001\u0980\u8001\u09b7\u8001\u09be\u8001\u09bf\u8001\u0a00"+
"\u8001\u0a00\u8001\u0a10\u8001\u0a13\u8001\u0a15\u8001\u0a17\u8001\u0a19"+
"\u8001\u0a35\u8001\u0a60\u8001\u0a7c\u8001\u0a80\u8001\u0a9c\u8001\u0ac0"+
"\u8001\u0ac7\u8001\u0ac9\u8001\u0ae4\u8001\u0b00\u8001\u0b35\u8001\u0b40"+
"\u8001\u0b55\u8001\u0b60\u8001\u0b72\u8001\u0b80\u8001\u0b91\u8001\u0c00"+
"\u8001\u0c48\u8001\u0c80\u8001\u0cb2\u8001\u0cc0\u8001\u0cf2\u8001\u0d00"+
"\u8001\u0d23\u8001\u0e80\u8001\u0ea9\u8001\u0eb0\u8001\u0eb1\u8001\u0f00"+
"\u8001\u0f1c\u8001\u0f27\u8001\u0f27\u8001\u0f30\u8001\u0f45\u8001\u0f70"+
"\u8001\u0f81\u8001\u0fb0\u8001\u0fc4\u8001\u0fe0\u8001\u0ff6\u8001\u1003"+
"\u8001\u1037\u8001\u1071\u8001\u1072\u8001\u1075\u8001\u1075\u8001\u1083"+
"\u8001\u10af\u8001\u10d0\u8001\u10e8\u8001\u1103\u8001\u1126\u8001\u1144"+
"\u8001\u1144\u8001\u1147\u8001\u1147\u8001\u1150\u8001\u1172\u8001\u1176"+
"\u8001\u1176\u8001\u1183\u8001\u11b2\u8001\u11c1\u8001\u11c4\u8001\u11da"+
"\u8001\u11da\u8001\u11dc\u8001\u11dc\u8001\u1200\u8001\u1211\u8001\u1213"+
"\u8001\u122b\u8001\u123f\u8001\u1240\u8001\u1280\u8001\u1286\u8001\u1288"+
"\u8001\u1288\u8001\u128a\u8001\u128d\u8001\u128f\u8001\u129d\u8001\u129f"+
"\u8001\u12a8\u8001\u12b0\u8001\u12de\u8001\u1305\u8001\u130c\u8001\u130f"+
"\u8001\u1310\u8001\u1313\u8001\u1328\u8001\u132a\u8001\u1330\u8001\u1332"+
"\u8001\u1333\u8001\u1335\u8001\u1339\u8001\u133d\u8001\u133d\u8001\u1350"+
"\u8001\u1350\u8001\u135d\u8001\u1361\u8001\u1400\u8001\u1434\u8001\u1447"+
"\u8001\u144a\u8001\u145f\u8001\u1461\u8001\u1480\u8001\u14af\u8001\u14c4"+
"\u8001\u14c5\u8001\u14c7\u8001\u14c7\u8001\u1580\u8001\u15ae\u8001\u15d8"+
"\u8001\u15db\u8001\u1600\u8001\u162f\u8001\u1644\u8001\u1644\u8001\u1680"+
"\u8001\u16aa\u8001\u16b8\u8001\u16b8\u8001\u1700\u8001\u171a\u8001\u1740"+
"\u8001\u1746\u8001\u1800\u8001\u182b\u8001\u18a0\u8001\u18df\u8001\u18ff"+
"\u8001\u1906\u8001\u1909\u8001\u1909\u8001\u190c\u8001\u1913\u8001\u1915"+
"\u8001\u1916\u8001\u1918\u8001\u192f\u8001\u193f\u8001\u193f\u8001\u1941"+
"\u8001\u1941\u8001\u19a0\u8001\u19a7\u8001\u19aa\u8001\u19d0\u8001\u19e1"+
"\u8001\u19e1\u8001\u19e3\u8001\u19e3\u8001\u1a00\u8001\u1a00\u8001\u1a0b"+
"\u8001\u1a32\u8001\u1a3a\u8001\u1a3a\u8001\u1a50\u8001\u1a50\u8001\u1a5c"+
"\u8001\u1a89\u8001\u1a9d\u8001\u1a9d\u8001\u1ab0\u8001\u1af8\u8001\u1c00"+
"\u8001\u1c08\u8001\u1c0a\u8001\u1c2e\u8001\u1c40\u8001\u1c40\u8001\u1c72"+
"\u8001\u1c8f\u8001\u1d00\u8001\u1d06\u8001\u1d08\u8001\u1d09\u8001\u1d0b"+
"\u8001\u1d30\u8001\u1d46\u8001\u1d46\u8001\u1d60\u8001\u1d65\u8001\u1d67"+
"\u8001\u1d68\u8001\u1d6a\u8001\u1d89\u8001\u1d98\u8001\u1d98\u8001\u1ee0"+
"\u8001\u1ef2\u8001\u1f02\u8001\u1f02\u8001\u1f04\u8001\u1f10\u8001\u1f12"+
"\u8001\u1f33\u8001\u1fb0\u8001\u1fb0\u8001\u2000\u8001\u2399\u8001\u2480"+
"\u8001\u2543\u8001\u2f90\u8001\u2ff0\u8001\u3000\u8001\u342f\u8001\u3441"+
"\u8001\u3446\u8001\u4400\u8001\u4646\u8001\u6800\u8001\u6a38\u8001\u6a40"+
"\u8001\u6a5e\u8001\u6a70\u8001\u6abe\u8001\u6ad0\u8001\u6aed\u8001\u6b00"+
"\u8001\u6b2f\u8001\u6b40\u8001\u6b43\u8001\u6b63\u8001\u6b77\u8001\u6b7d"+
"\u8001\u6b8f\u8001\u6e40\u8001\u6e7f\u8001\u6f00\u8001\u6f4a\u8001\u6f50"+
"\u8001\u6f50\u8001\u6f93\u8001\u6f9f\u8001\u6fe0\u8001\u6fe1\u8001\u6fe3"+
"\u8001\u6fe3\u8001\u7000\u8001\u87f7\u8001\u8800\u8001\u8cd5\u8001\u8d00"+
"\u8001\u8d08\u8001\uaff0\u8001\uaff3\u8001\uaff5\u8001\uaffb\u8001\uaffd"+
"\u8001\uaffe\u8001\ub000\u8001\ub122\u8001\ub132\u8001\ub132\u8001\ub150"+
"\u8001\ub152\u8001\ub155\u8001\ub155\u8001\ub164\u8001\ub167\u8001\ub170"+
"\u8001\ub2fb\u8001\ubc00\u8001\ubc6a\u8001\ubc70\u8001\ubc7c\u8001\ubc80"+
"\u8001\ubc88\u8001\ubc90\u8001\ubc99\u8001\ud400\u8001\ud454\u8001\ud456"+
"\u8001\ud49c\u8001\ud49e\u8001\ud49f\u8001\ud4a2\u8001\ud4a2\u8001\ud4a5"+
"\u8001\ud4a6\u8001\ud4a9\u8001\ud4ac\u8001\ud4ae\u8001\ud4b9\u8001\ud4bb"+
"\u8001\ud4bb\u8001\ud4bd\u8001\ud4c3\u8001\ud4c5\u8001\ud505\u8001\ud507"+
"\u8001\ud50a\u8001\ud50d\u8001\ud514\u8001\ud516\u8001\ud51c\u8001\ud51e"+
"\u8001\ud539\u8001\ud53b\u8001\ud53e\u8001\ud540\u8001\ud544\u8001\ud546"+
"\u8001\ud546\u8001\ud54a\u8001\ud550\u8001\ud552\u8001\ud6a5\u8001\ud6a8"+
"\u8001\ud6c0\u8001\ud6c2\u8001\ud6da\u8001\ud6dc\u8001\ud6fa\u8001\ud6fc"+
"\u8001\ud714\u8001\ud716\u8001\ud734\u8001\ud736\u8001\ud74e\u8001\ud750"+
"\u8001\ud76e\u8001\ud770\u8001\ud788\u8001\ud78a\u8001\ud7a8\u8001\ud7aa"+
"\u8001\ud7c2\u8001\ud7c4\u8001\ud7cb\u8001\udf00\u8001\udf1e\u8001\udf25"+
"\u8001\udf2a\u8001\ue030\u8001\ue06d\u8001\ue100\u8001\ue12c\u8001\ue137"+
"\u8001\ue13d\u8001\ue14e\u8001\ue14e\u8001\ue290\u8001\ue2ad\u8001\ue2c0"+
"\u8001\ue2eb\u8001\ue4d0\u8001\ue4eb\u8001\ue7e0\u8001\ue7e6\u8001\ue7e8"+
"\u8001\ue7eb\u8001\ue7ed\u8001\ue7ee\u8001\ue7f0\u8001\ue7fe\u8001\ue800"+
"\u8001\ue8c4\u8001\ue900\u8001\ue943\u8001\ue94b\u8001\ue94b\u8001\uee00"+
"\u8001\uee03\u8001\uee05\u8001\uee1f\u8001\uee21\u8001\uee22\u8001\uee24"+
"\u8001\uee24\u8001\uee27\u8001\uee27\u8001\uee29\u8001\uee32\u8001\uee34"+
"\u8001\uee37\u8001\uee39\u8001\uee39\u8001\uee3b\u8001\uee3b\u8001\uee42"+
"\u8001\uee42\u8001\uee47\u8001\uee47\u8001\uee49\u8001\uee49\u8001\uee4b"+
"\u8001\uee4b\u8001\uee4d\u8001\uee4f\u8001\uee51\u8001\uee52\u8001\uee54"+
"\u8001\uee54\u8001\uee57\u8001\uee57\u8001\uee59\u8001\uee59\u8001\uee5b"+
"\u8001\uee5b\u8001\uee5d\u8001\uee5d\u8001\uee5f\u8001\uee5f\u8001\uee61"+
"\u8001\uee62\u8001\uee64\u8001\uee64\u8001\uee67\u8001\uee6a\u8001\uee6c"+
"\u8001\uee72\u8001\uee74\u8001\uee77\u8001\uee79\u8001\uee7c\u8001\uee7e"+
"\u8001\uee7e\u8001\uee80\u8001\uee89\u8001\uee8b\u8001\uee9b\u8001\ueea1"+
"\u8001\ueea3\u8001\ueea5\u8001\ueea9\u8001\ueeab\u8001\ueebb\u8002\u0000"+
"\u8002\ua6df\u8002\ua700\u8002\ub739\u8002\ub740\u8002\ub81d\u8002\ub820"+
"\u8002\ucea1\u8002\uceb0\u8002\uebe0\u8002\uf800\u8002\ufa1d\u8003\u0000"+
"\u8003\u134a\u8003\u1350\u8003\u23af\u0002\u0000\n\n``\u01ed\u0000\u0002"+
"\u0001\u0000\u0000\u0000\u0000\u0006\u0001\u0000\u0000\u0000\u0000\b\u0001"+
"\u0000\u0000\u0000\u0000\n\u0001\u0000\u0000\u0000\u0000\f\u0001\u0000"+
"\u0000\u0000\u0000\u000e\u0001\u0000\u0000\u0000\u0000\u0010\u0001\u0000"+
"\u0000\u0000\u0000\u0012\u0001\u0000\u0000\u0000\u0000\u0014\u0001\u0000"+
"\u0000\u0000\u0000\u0016\u0001\u0000\u0000\u0000\u0000\u0018\u0001\u0000"+
"\u0000\u0000\u0000\u001a\u0001\u0000\u0000\u0000\u0000\u001c\u0001\u0000"+
"\u0000\u0000\u0000\u001e\u0001\u0000\u0000\u0000\u0000 \u0001\u0000\u0000"+
"\u0000\u0000\"\u0001\u0000\u0000\u0000\u0000$\u0001\u0000\u0000\u0000"+
"\u0000&\u0001\u0000\u0000\u0000\u0000(\u0001\u0000\u0000\u0000\u0000*"+
"\u0001\u0000\u0000\u0000\u0000,\u0001\u0000\u0000\u0000\u0000.\u0001\u0000"+
"\u0000\u0000\u00000\u0001\u0000\u0000\u0000\u00002\u0001\u0000\u0000\u0000"+
"\u00004\u0001\u0000\u0000\u0000\u00006\u0001\u0000\u0000\u0000\u00008"+
"\u0001\u0000\u0000\u0000\u0000:\u0001\u0000\u0000\u0000\u0000<\u0001\u0000"+
"\u0000\u0000\u0000>\u0001\u0000\u0000\u0000\u0000@\u0001\u0000\u0000\u0000"+
"\u0000B\u0001\u0000\u0000\u0000\u0000D\u0001\u0000\u0000\u0000\u0000F"+
"\u0001\u0000\u0000\u0000\u0000H\u0001\u0000\u0000\u0000\u0000J\u0001\u0000"+
"\u0000\u0000\u0000L\u0001\u0000\u0000\u0000\u0000N\u0001\u0000\u0000\u0000"+
"\u0000P\u0001\u0000\u0000\u0000\u0000R\u0001\u0000\u0000\u0000\u0000T"+
"\u0001\u0000\u0000\u0000\u0000V\u0001\u0000\u0000\u0000\u0000X\u0001\u0000"+
"\u0000\u0000\u0000Z\u0001\u0000\u0000\u0000\u0000\\\u0001\u0000\u0000"+
"\u0000\u0000^\u0001\u0000\u0000\u0000\u0000`\u0001\u0000\u0000\u0000\u0000"+
"b\u0001\u0000\u0000\u0000\u0000d\u0001\u0000\u0000\u0000\u0000f\u0001"+
"\u0000\u0000\u0000\u0000h\u0001\u0000\u0000\u0000\u0000j\u0001\u0000\u0000"+
"\u0000\u0000l\u0001\u0000\u0000\u0000\u0000n\u0001\u0000\u0000\u0000\u0000"+
"p\u0001\u0000\u0000\u0000\u0000r\u0001\u0000\u0000\u0000\u0000t\u0001"+
"\u0000\u0000\u0000\u0000v\u0001\u0000\u0000\u0000\u0000x\u0001\u0000\u0000"+
"\u0000\u0000z\u0001\u0000\u0000\u0000\u0000|\u0001\u0000\u0000\u0000\u0001"+
"\u008e\u0001\u0000\u0000\u0000\u0001\u0090\u0001\u0000\u0000\u0000\u0001"+
"\u0092\u0001\u0000\u0000\u0000\u0001\u0094\u0001\u0000\u0000\u0000\u0002"+
"\u0096\u0001\u0000\u0000\u0000\u0004\u009b\u0001\u0000\u0000\u0000\u0006"+
"\u009e\u0001\u0000\u0000\u0000\b\u00a4\u0001\u0000\u0000\u0000\n\u00a7"+
"\u0001\u0000\u0000\u0000\f\u00a9\u0001\u0000\u0000\u0000\u000e\u00ac\u0001"+
"\u0000\u0000\u0000\u0010\u00ae\u0001\u0000\u0000\u0000\u0012\u00b0\u0001"+
"\u0000\u0000\u0000\u0014\u00b2\u0001\u0000\u0000\u0000\u0016\u00b4\u0001"+
"\u0000\u0000\u0000\u0018\u00b6\u0001\u0000\u0000\u0000\u001a\u00b8\u0001"+
"\u0000\u0000\u0000\u001c\u00ba\u0001\u0000\u0000\u0000\u001e\u00bc\u0001"+
"\u0000\u0000\u0000 \u00be\u0001\u0000\u0000\u0000\"\u00c0\u0001\u0000"+
"\u0000\u0000$\u00c2\u0001\u0000\u0000\u0000&\u00c4\u0001\u0000\u0000\u0000"+
"(\u00c6\u0001\u0000\u0000\u0000*\u00c9\u0001\u0000\u0000\u0000,\u00cb"+
"\u0001\u0000\u0000\u0000.\u00ce\u0001\u0000\u0000\u00000\u00d1\u0001\u0000"+
"\u0000\u00002\u00d3\u0001\u0000\u0000\u00004\u00d6\u0001\u0000\u0000\u0000"+
"6\u00d8\u0001\u0000\u0000\u00008\u00db\u0001\u0000\u0000\u0000:\u00dd"+
"\u0001\u0000\u0000\u0000<\u00e0\u0001\u0000\u0000\u0000>\u00e3\u0001\u0000"+
"\u0000\u0000@\u00e6\u0001\u0000\u0000\u0000B\u00e9\u0001\u0000\u0000\u0000"+
"D\u00eb\u0001\u0000\u0000\u0000F\u00ee\u0001\u0000\u0000\u0000H\u00f1"+
"\u0001\u0000\u0000\u0000J\u00f3\u0001\u0000\u0000\u0000L\u00f6\u0001\u0000"+
"\u0000\u0000N\u00f8\u0001\u0000\u0000\u0000P\u00fd\u0001\u0000\u0000\u0000"+
"R\u0102\u0001\u0000\u0000\u0000T\u0106\u0001\u0000\u0000\u0000V\u0109"+
"\u0001\u0000\u0000\u0000X\u010d\u0001\u0000\u0000\u0000Z\u0112\u0001\u0000"+
"\u0000\u0000\\\u0118\u0001\u0000\u0000\u0000^\u011c\u0001\u0000\u0000"+
"\u0000`\u0121\u0001\u0000\u0000\u0000b\u0123\u0001\u0000\u0000\u0000d"+
"\u012b\u0001\u0000\u0000\u0000f\u012e\u0001\u0000\u0000\u0000h\u0131\u0001"+
"\u0000\u0000\u0000j\u0134\u0001\u0000\u0000\u0000l\u0137\u0001\u0000\u0000"+
"\u0000n\u013a\u0001\u0000\u0000\u0000p\u013f\u0001\u0000\u0000\u0000r"+
"\u014b\u0001\u0000\u0000\u0000t\u017a\u0001\u0000\u0000\u0000v\u017d\u0001"+
"\u0000\u0000\u0000x\u0186\u0001\u0000\u0000\u0000z\u0188\u0001\u0000\u0000"+
"\u0000|\u0193\u0001\u0000\u0000\u0000~\u019e\u0001\u0000\u0000\u0000\u0080"+
"\u01a0\u0001\u0000\u0000\u0000\u0082\u01a2\u0001\u0000\u0000\u0000\u0084"+
"\u01bc\u0001\u0000\u0000\u0000\u0086\u01be\u0001\u0000\u0000\u0000\u0088"+
"\u01c0\u0001\u0000\u0000\u0000\u008a\u01c3\u0001\u0000\u0000\u0000\u008c"+
"\u01c5\u0001\u0000\u0000\u0000\u008e\u01c7\u0001\u0000\u0000\u0000\u0090"+
"\u01ca\u0001\u0000\u0000\u0000\u0092\u01d1\u0001\u0000\u0000\u0000\u0094"+
"\u01d3\u0001\u0000\u0000\u0000\u0096\u0097\u0005;\u0000\u0000\u0097\u0003"+
"\u0001\u0000\u0000\u0000\u0098\u0099\u0005\r\u0000\u0000\u0099\u009c\u0005"+
"\n\u0000\u0000\u009a\u009c\u0007\u0000\u0000\u0000\u009b\u0098\u0001\u0000"+
"\u0000\u0000\u009b\u009a\u0001\u0000\u0000\u0000\u009c\u0005\u0001\u0000"+
"\u0000\u0000\u009d\u009f\u0007\u0001\u0000\u0000\u009e\u009d\u0001\u0000"+
"\u0000\u0000\u009f\u00a0\u0001\u0000\u0000\u0000\u00a0\u009e\u0001\u0000"+
"\u0000\u0000\u00a0\u00a1\u0001\u0000\u0000\u0000\u00a1\u00a2\u0001\u0000"+
"\u0000\u0000\u00a2\u00a3\u0006\u0002\u0000\u0000\u00a3\u0007\u0001\u0000"+
"\u0000\u0000\u00a4\u00a5\u0005+\u0000\u0000\u00a5\u00a6\u0005+\u0000\u0000"+
"\u00a6\t\u0001\u0000\u0000\u0000\u00a7\u00a8\u0005+\u0000\u0000\u00a8"+
"\u000b\u0001\u0000\u0000\u0000\u00a9\u00aa\u0005-\u0000\u0000\u00aa\u00ab"+
"\u0005-\u0000\u0000\u00ab\r\u0001\u0000\u0000\u0000\u00ac\u00ad\u0005"+
"-\u0000\u0000\u00ad\u000f\u0001\u0000\u0000\u0000\u00ae\u00af\u0005:\u0000"+
"\u0000\u00af\u0011\u0001\u0000\u0000\u0000\u00b0\u00b1\u0005.\u0000\u0000"+
"\u00b1\u0013\u0001\u0000\u0000\u0000\u00b2\u00b3\u0005,\u0000\u0000\u00b3"+
"\u0015\u0001\u0000\u0000\u0000\u00b4\u00b5\u0005*\u0000\u0000\u00b5\u0017"+
"\u0001\u0000\u0000\u0000\u00b6\u00b7\u0005/\u0000\u0000\u00b7\u0019\u0001"+
"\u0000\u0000\u0000\u00b8\u00b9\u0005%\u0000\u0000\u00b9\u001b\u0001\u0000"+
"\u0000\u0000\u00ba\u00bb\u0005(\u0000\u0000\u00bb\u001d\u0001\u0000\u0000"+
"\u0000\u00bc\u00bd\u0005)\u0000\u0000\u00bd\u001f\u0001\u0000\u0000\u0000"+
"\u00be\u00bf\u0005[\u0000\u0000\u00bf!\u0001\u0000\u0000\u0000\u00c0\u00c1"+
"\u0005]\u0000\u0000\u00c1#\u0001\u0000\u0000\u0000\u00c2\u00c3\u0005#"+
"\u0000\u0000\u00c3%\u0001\u0000\u0000\u0000\u00c4\u00c5\u0005@\u0000\u0000"+
"\u00c5\'\u0001\u0000\u0000\u0000\u00c6\u00c7\u0005^\u0000\u0000\u00c7"+
"\u00c8\u0005[\u0000\u0000\u00c8)\u0001\u0000\u0000\u0000\u00c9\u00ca\u0005"+
"^\u0000\u0000\u00ca+\u0001\u0000\u0000\u0000\u00cb\u00cc\u0005!\u0000"+
"\u0000\u00cc\u00cd\u0005=\u0000\u0000\u00cd-\u0001\u0000\u0000\u0000\u00ce"+
"\u00cf\u0005!\u0000\u0000\u00cf\u00d0\u0005[\u0000\u0000\u00d0/\u0001"+
"\u0000\u0000\u0000\u00d1\u00d2\u0005!\u0000\u0000\u00d21\u0001\u0000\u0000"+
"\u0000\u00d3\u00d4\u0005=\u0000\u0000\u00d4\u00d5\u0005=\u0000\u0000\u00d5"+
"3\u0001\u0000\u0000\u0000\u00d6\u00d7\u0005=\u0000\u0000\u00d75\u0001"+
"\u0000\u0000\u0000\u00d8\u00d9\u0005&\u0000\u0000\u00d9\u00da\u0005&\u0000"+
"\u0000\u00da7\u0001\u0000\u0000\u0000\u00db\u00dc\u0005&\u0000\u0000\u00dc"+
"9\u0001\u0000\u0000\u0000\u00dd\u00de\u0005|\u0000\u0000\u00de\u00df\u0005"+
"|\u0000\u0000\u00df;\u0001\u0000\u0000\u0000\u00e0\u00e1\u0005?\u0000"+
"\u0000\u00e1\u00e2\u0005[\u0000\u0000\u00e2=\u0001\u0000\u0000\u0000\u00e3"+
"\u00e4\u0005?\u0000\u0000\u00e4\u00e5\u0005:\u0000\u0000\u00e5?\u0001"+
"\u0000\u0000\u0000\u00e6\u00e7\u0005?\u0000\u0000\u00e7\u00e8\u0005.\u0000"+
"\u0000\u00e8A\u0001\u0000\u0000\u0000\u00e9\u00ea\u0005?\u0000\u0000\u00ea"+
"C\u0001\u0000\u0000\u0000\u00eb\u00ec\u0005$\u0000\u0000\u00ec\u00ed\u0005"+
"[\u0000\u0000\u00edE\u0001\u0000\u0000\u0000\u00ee\u00ef\u0005>\u0000"+
"\u0000\u00ef\u00f0\u0005=\u0000\u0000\u00f0G\u0001\u0000\u0000\u0000\u00f1"+
"\u00f2\u0005>\u0000\u0000\u00f2I\u0001\u0000\u0000\u0000\u00f3\u00f4\u0005"+
"<\u0000\u0000\u00f4\u00f5\u0005=\u0000\u0000\u00f5K\u0001\u0000\u0000"+
"\u0000\u00f6\u00f7\u0005<\u0000\u0000\u00f7M\u0001\u0000\u0000\u0000\u00f8"+
"\u00f9\u0005{\u0000\u0000\u00f9\u00fa\u0006&\u0001\u0000\u00fa\u00fb\u0001"+
"\u0000\u0000\u0000\u00fb\u00fc\u0006&\u0002\u0000\u00fcO\u0001\u0000\u0000"+
"\u0000\u00fd\u00fe\u0005}\u0000\u0000\u00fe\u00ff\u0006\'\u0003\u0000"+
"\u00ff\u0100\u0001\u0000\u0000\u0000\u0100\u0101\u0006\'\u0004\u0000\u0101"+
"Q\u0001\u0000\u0000\u0000\u0102\u0103\u0005`\u0000\u0000\u0103\u0104\u0001"+
"\u0000\u0000\u0000\u0104\u0105\u0006(\u0005\u0000\u0105S\u0001\u0000\u0000"+
"\u0000\u0106\u0107\u0005o\u0000\u0000\u0107\u0108\u0005r\u0000\u0000\u0108"+
"U\u0001\u0000\u0000\u0000\u0109\u010a\u0005a\u0000\u0000\u010a\u010b\u0005"+
"n\u0000\u0000\u010b\u010c\u0005d\u0000\u0000\u010cW\u0001\u0000\u0000"+
"\u0000\u010d\u010e\u0005t\u0000\u0000\u010e\u010f\u0005r\u0000\u0000\u010f"+
"\u0110\u0005u\u0000\u0000\u0110\u0111\u0005e\u0000\u0000\u0111Y\u0001"+
"\u0000\u0000\u0000\u0112\u0113\u0005f\u0000\u0000\u0113\u0114\u0005a\u0000"+
"\u0000\u0114\u0115\u0005l\u0000\u0000\u0115\u0116\u0005s\u0000\u0000\u0116"+
"\u0117\u0005e\u0000\u0000\u0117[\u0001\u0000\u0000\u0000\u0118\u0119\u0005"+
"n\u0000\u0000\u0119\u011a\u0005e\u0000\u0000\u011a\u011b\u0005w\u0000"+
"\u0000\u011b]\u0001\u0000\u0000\u0000\u011c\u011d\u0005n\u0000\u0000\u011d"+
"\u011e\u0005u\u0000\u0000\u011e\u011f\u0005l\u0000\u0000\u011f\u0120\u0005"+
"l\u0000\u0000\u0120_\u0001\u0000\u0000\u0000\u0121\u0122\u0005T\u0000"+
"\u0000\u0122a\u0001\u0000\u0000\u0000\u0123\u0124\u0005m\u0000\u0000\u0124"+
"\u0125\u0005a\u0000\u0000\u0125\u0126\u0005t\u0000\u0000\u0126\u0127\u0005"+
"c\u0000\u0000\u0127\u0128\u0005h\u0000\u0000\u0128\u0129\u0005e\u0000"+
"\u0000\u0129\u012a\u0005s\u0000\u0000\u012ac\u0001\u0000\u0000\u0000\u012b"+
"\u012c\u0005g\u0000\u0000\u012c\u012d\u0005t\u0000\u0000\u012de\u0001"+
"\u0000\u0000\u0000\u012e\u012f\u0005g\u0000\u0000\u012f\u0130\u0005e\u0000"+
"\u0000\u0130g\u0001\u0000\u0000\u0000\u0131\u0132\u0005l\u0000\u0000\u0132"+
"\u0133\u0005e\u0000\u0000\u0133i\u0001\u0000\u0000\u0000\u0134\u0135\u0005"+
"l\u0000\u0000\u0135\u0136\u0005t\u0000\u0000\u0136k\u0001\u0000\u0000"+
"\u0000\u0137\u0138\u0005e\u0000\u0000\u0138\u0139\u0005q\u0000\u0000\u0139"+
"m\u0001\u0000\u0000\u0000\u013a\u013b\u0005n\u0000\u0000\u013b\u013c\u0005"+
"e\u0000\u0000\u013co\u0001\u0000\u0000\u0000\u013d\u0140\u0003\u008aD"+
"\u0000\u013e\u0140\u0005_\u0000\u0000\u013f\u013d\u0001\u0000\u0000\u0000"+
"\u013f\u013e\u0001\u0000\u0000\u0000\u0140\u0146\u0001\u0000\u0000\u0000"+
"\u0141\u0145\u0003\u008aD\u0000\u0142\u0145\u0003\u008cE\u0000\u0143\u0145"+
"\u0007\u0002\u0000\u0000\u0144\u0141\u0001\u0000\u0000\u0000\u0144\u0142"+
"\u0001\u0000\u0000\u0000\u0144\u0143\u0001\u0000\u0000\u0000\u0145\u0148"+
"\u0001\u0000\u0000\u0000\u0146\u0144\u0001\u0000\u0000\u0000\u0146\u0147"+
"\u0001\u0000\u0000\u0000\u0147q\u0001\u0000\u0000\u0000\u0148\u0146\u0001"+
"\u0000\u0000\u0000\u0149\u014c\u0003t9\u0000\u014a\u014c\u0003v:\u0000"+
"\u014b\u0149\u0001\u0000\u0000\u0000\u014b\u014a\u0001\u0000\u0000\u0000"+
"\u014cs\u0001\u0000\u0000\u0000\u014d\u014f\u0005.\u0000\u0000\u014e\u0150"+
"\u0003\u0082@\u0000\u014f\u014e\u0001\u0000\u0000\u0000\u0150\u0151\u0001"+
"\u0000\u0000\u0000\u0151\u014f\u0001\u0000\u0000\u0000\u0151\u0152\u0001"+
"\u0000\u0000\u0000\u0152\u0154\u0001\u0000\u0000\u0000\u0153\u0155\u0003"+
"\u0084A\u0000\u0154\u0153\u0001\u0000\u0000\u0000\u0154\u0155\u0001\u0000"+
"\u0000\u0000\u0155\u0157\u0001\u0000\u0000\u0000\u0156\u0158\u0003\u0088"+
"C\u0000\u0157\u0156\u0001\u0000\u0000\u0000\u0157\u0158\u0001\u0000\u0000"+
"\u0000\u0158\u017b\u0001\u0000\u0000\u0000\u0159\u015b\u0003\u0082@\u0000"+
"\u015a\u0159\u0001\u0000\u0000\u0000\u015b\u015c\u0001\u0000\u0000\u0000"+
"\u015c\u015a\u0001\u0000\u0000\u0000\u015c\u015d\u0001\u0000\u0000\u0000"+
"\u015d\u015e\u0001\u0000\u0000\u0000\u015e\u0160\u0005.\u0000\u0000\u015f"+
"\u0161\u0003\u0082@\u0000\u0160\u015f\u0001\u0000\u0000\u0000\u0161\u0162"+
"\u0001\u0000\u0000\u0000\u0162\u0160\u0001\u0000\u0000\u0000\u0162\u0163"+
"\u0001\u0000\u0000\u0000\u0163\u0165\u0001\u0000\u0000\u0000\u0164\u0166"+
"\u0003\u0084A\u0000\u0165\u0164\u0001\u0000\u0000\u0000\u0165\u0166\u0001"+
"\u0000\u0000\u0000\u0166\u0168\u0001\u0000\u0000\u0000\u0167\u0169\u0003"+
"\u0088C\u0000\u0168\u0167\u0001\u0000\u0000\u0000\u0168\u0169\u0001\u0000"+
"\u0000\u0000\u0169\u017b\u0001\u0000\u0000\u0000\u016a\u016c\u0003\u0082"+
"@\u0000\u016b\u016a\u0001\u0000\u0000\u0000\u016c\u016d\u0001\u0000\u0000"+
"\u0000\u016d\u016b\u0001\u0000\u0000\u0000\u016d\u016e\u0001\u0000\u0000"+
"\u0000\u016e\u016f\u0001\u0000\u0000\u0000\u016f\u0171\u0003\u0084A\u0000"+
"\u0170\u0172\u0003\u0088C\u0000\u0171\u0170\u0001\u0000\u0000\u0000\u0171"+
"\u0172\u0001\u0000\u0000\u0000\u0172\u017b\u0001\u0000\u0000\u0000\u0173"+
"\u0175\u0003\u0082@\u0000\u0174\u0173\u0001\u0000\u0000\u0000\u0175\u0176"+
"\u0001\u0000\u0000\u0000\u0176\u0174\u0001\u0000\u0000\u0000\u0176\u0177"+
"\u0001\u0000\u0000\u0000\u0177\u0178\u0001\u0000\u0000\u0000\u0178\u0179"+
"\u0003\u0088C\u0000\u0179\u017b\u0001\u0000\u0000\u0000\u017a\u014d\u0001"+
"\u0000\u0000\u0000\u017a\u015a\u0001\u0000\u0000\u0000\u017a\u016b\u0001"+
"\u0000\u0000\u0000\u017a\u0174\u0001\u0000\u0000\u0000\u017bu\u0001\u0000"+
"\u0000\u0000\u017c\u017e\u0003\u0082@\u0000\u017d\u017c\u0001\u0000\u0000"+
"\u0000\u017e\u017f\u0001\u0000\u0000\u0000\u017f\u017d\u0001\u0000\u0000"+
"\u0000\u017f\u0180\u0001\u0000\u0000\u0000\u0180\u0182\u0001\u0000\u0000"+
"\u0000\u0181\u0183\u0003~>\u0000\u0182\u0181\u0001\u0000\u0000\u0000\u0182"+
"\u0183\u0001\u0000\u0000\u0000\u0183w\u0001\u0000\u0000\u0000\u0184\u0187"+
"\u0003z<\u0000\u0185\u0187\u0003|=\u0000\u0186\u0184\u0001\u0000\u0000"+
"\u0000\u0186\u0185\u0001\u0000\u0000\u0000\u0187y\u0001\u0000\u0000\u0000"+
"\u0188\u018e\u0005\'\u0000\u0000\u0189\u018a\u0005\'\u0000\u0000\u018a"+
"\u018d\u0005\'\u0000\u0000\u018b\u018d\b\u0003\u0000\u0000\u018c\u0189"+
"\u0001\u0000\u0000\u0000\u018c\u018b\u0001\u0000\u0000\u0000\u018d\u0190"+
"\u0001\u0000\u0000\u0000\u018e\u018c\u0001\u0000\u0000\u0000\u018e\u018f"+
"\u0001\u0000\u0000\u0000\u018f\u0191\u0001\u0000\u0000\u0000\u0190\u018e"+
"\u0001\u0000\u0000\u0000\u0191\u0192\u0005\'\u0000\u0000\u0192{\u0001"+
"\u0000\u0000\u0000\u0193\u0199\u0005\"\u0000\u0000\u0194\u0195\u0005\""+
"\u0000\u0000\u0195\u0198\u0005\"\u0000\u0000\u0196\u0198\b\u0004\u0000"+
"\u0000\u0197\u0194\u0001\u0000\u0000\u0000\u0197\u0196\u0001\u0000\u0000"+
"\u0000\u0198\u019b\u0001\u0000\u0000\u0000\u0199\u0197\u0001\u0000\u0000"+
"\u0000\u0199\u019a\u0001\u0000\u0000\u0000\u019a\u019c\u0001\u0000\u0000"+
"\u0000\u019b\u0199\u0001\u0000\u0000\u0000\u019c\u019d\u0005\"\u0000\u0000"+
"\u019d}\u0001\u0000\u0000\u0000\u019e\u019f\u0007\u0005\u0000\u0000\u019f"+
"\u007f\u0001\u0000\u0000\u0000\u01a0\u01a1\u0007\u0006\u0000\u0000\u01a1"+
"\u0081\u0001\u0000\u0000\u0000\u01a2\u01a3\u0007\u0007\u0000\u0000\u01a3"+
"\u0083\u0001\u0000\u0000\u0000\u01a4\u01a8\u0005e\u0000\u0000\u01a5\u01a7"+
"\u0003\u0086B\u0000\u01a6\u01a5\u0001\u0000\u0000\u0000\u01a7\u01aa\u0001"+
"\u0000\u0000\u0000\u01a8\u01a6\u0001\u0000\u0000\u0000\u01a8\u01a9\u0001"+
"\u0000\u0000\u0000\u01a9\u01ac\u0001\u0000\u0000\u0000\u01aa\u01a8\u0001"+
"\u0000\u0000\u0000\u01ab\u01ad\u0003\u0082@\u0000\u01ac\u01ab\u0001\u0000"+
"\u0000\u0000\u01ad\u01ae\u0001\u0000\u0000\u0000\u01ae\u01ac\u0001\u0000"+
"\u0000\u0000\u01ae\u01af\u0001\u0000\u0000\u0000\u01af\u01bd\u0001\u0000"+
"\u0000\u0000\u01b0\u01b4\u0005E\u0000\u0000\u01b1\u01b3\u0003\u0086B\u0000"+
"\u01b2\u01b1\u0001\u0000\u0000\u0000\u01b3\u01b6\u0001\u0000\u0000\u0000"+
"\u01b4\u01b2\u0001\u0000\u0000\u0000\u01b4\u01b5\u0001\u0000\u0000\u0000"+
"\u01b5\u01b8\u0001\u0000\u0000\u0000\u01b6\u01b4\u0001\u0000\u0000\u0000"+
"\u01b7\u01b9\u0003\u0082@\u0000\u01b8\u01b7\u0001\u0000\u0000\u0000\u01b9"+
"\u01ba\u0001\u0000\u0000\u0000\u01ba\u01b8\u0001\u0000\u0000\u0000\u01ba"+
"\u01bb\u0001\u0000\u0000\u0000\u01bb\u01bd\u0001\u0000\u0000\u0000\u01bc"+
"\u01a4\u0001\u0000\u0000\u0000\u01bc\u01b0\u0001\u0000\u0000\u0000\u01bd"+
"\u0085\u0001\u0000\u0000\u0000\u01be\u01bf\u0007\b\u0000\u0000\u01bf\u0087"+
"\u0001\u0000\u0000\u0000\u01c0\u01c1\u0007\t\u0000\u0000\u01c1\u0089\u0001"+
"\u0000\u0000\u0000\u01c2\u01c4\u0007\n\u0000\u0000\u01c3\u01c2\u0001\u0000"+
"\u0000\u0000\u01c4\u008b\u0001\u0000\u0000\u0000\u01c5\u01c6\u0007\u0007"+
"\u0000\u0000\u01c6\u008d\u0001\u0000\u0000\u0000\u01c7\u01c8\u0005`\u0000"+
"\u0000\u01c8\u01c9\u0005`\u0000\u0000\u01c9\u008f\u0001\u0000\u0000\u0000"+
"\u01ca\u01cb\u0005#\u0000\u0000\u01cb\u01cc\u0005{\u0000\u0000\u01cc\u01cd"+
"\u0001\u0000\u0000\u0000\u01cd\u01ce\u0006G\u0006\u0000\u01ce\u01cf\u0001"+
"\u0000\u0000\u0000\u01cf\u01d0\u0006G\u0002\u0000\u01d0\u0091\u0001\u0000"+
"\u0000\u0000\u01d1\u01d2\b\u000b\u0000\u0000\u01d2\u0093\u0001\u0000\u0000"+
"\u0000\u01d3\u01d4\u0005`\u0000\u0000\u01d4\u01d5\u0001\u0000\u0000\u0000"+
"\u01d5\u01d6\u0006I\u0007\u0000\u01d6\u01d7\u0006I\u0004\u0000\u01d7\u0095"+
"\u0001\u0000\u0000\u0000 \u0000\u0001\u009b\u00a0\u013f\u0144\u0146\u014b"+
"\u0151\u0154\u0157\u015c\u0162\u0165\u0168\u016d\u0171\u0176\u017a\u017f"+
"\u0182\u0186\u018c\u018e\u0197\u0199\u01a8\u01ae\u01b4\u01ba\u01bc\u01c3"+
"\b\u0000\u0001\u0000\u0001&\u0000\u0005\u0000\u0000\u0001\'\u0001\u0004"+
"\u0000\u0000\u0005\u0001\u0000\u0001G\u0002\u0007(\u0000";
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);
}
}
}

View File

@@ -0,0 +1,352 @@
// Generated from SpelParser.g4 by ANTLR 4.13.1
package org.springframework.ide.vscode.parser.spel;
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
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 SpelParserListener},
* which can be extended to create a listener which only needs to handle a subset
* of the available methods.
*/
@SuppressWarnings("CheckReturnValue")
public class SpelParserBaseListener implements SpelParserListener {
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterScript(SpelParser.ScriptContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitScript(SpelParser.ScriptContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterSpelExpr(SpelParser.SpelExprContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitSpelExpr(SpelParser.SpelExprContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterNode(SpelParser.NodeContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitNode(SpelParser.NodeContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterNonDottedNode(SpelParser.NonDottedNodeContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitNonDottedNode(SpelParser.NonDottedNodeContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterDottedNode(SpelParser.DottedNodeContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitDottedNode(SpelParser.DottedNodeContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterFunctionOrVar(SpelParser.FunctionOrVarContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitFunctionOrVar(SpelParser.FunctionOrVarContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterMethodArgs(SpelParser.MethodArgsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitMethodArgs(SpelParser.MethodArgsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterArgs(SpelParser.ArgsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitArgs(SpelParser.ArgsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterMethodOrProperty(SpelParser.MethodOrPropertyContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitMethodOrProperty(SpelParser.MethodOrPropertyContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterProjection(SpelParser.ProjectionContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitProjection(SpelParser.ProjectionContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterSelection(SpelParser.SelectionContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitSelection(SpelParser.SelectionContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterStartNode(SpelParser.StartNodeContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitStartNode(SpelParser.StartNodeContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterLiteral(SpelParser.LiteralContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitLiteral(SpelParser.LiteralContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterParenspelExpr(SpelParser.ParenspelExprContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitParenspelExpr(SpelParser.ParenspelExprContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterTypeReference(SpelParser.TypeReferenceContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitTypeReference(SpelParser.TypeReferenceContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterPossiblyQualifiedId(SpelParser.PossiblyQualifiedIdContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitPossiblyQualifiedId(SpelParser.PossiblyQualifiedIdContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterNullReference(SpelParser.NullReferenceContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitNullReference(SpelParser.NullReferenceContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterConstructorReference(SpelParser.ConstructorReferenceContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitConstructorReference(SpelParser.ConstructorReferenceContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterConstructorArgs(SpelParser.ConstructorArgsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitConstructorArgs(SpelParser.ConstructorArgsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterInlineListOrMap(SpelParser.InlineListOrMapContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitInlineListOrMap(SpelParser.InlineListOrMapContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterListBindings(SpelParser.ListBindingsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitListBindings(SpelParser.ListBindingsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterListBinding(SpelParser.ListBindingContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitListBinding(SpelParser.ListBindingContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterMapBindings(SpelParser.MapBindingsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitMapBindings(SpelParser.MapBindingsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterMapBinding(SpelParser.MapBindingContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitMapBinding(SpelParser.MapBindingContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterBeanReference(SpelParser.BeanReferenceContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitBeanReference(SpelParser.BeanReferenceContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterEveryRule(ParserRuleContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitEveryRule(ParserRuleContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void visitTerminal(TerminalNode node) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void visitErrorNode(ErrorNode node) { }
}

View File

@@ -0,0 +1,272 @@
// Generated from SpelParser.g4 by ANTLR 4.13.1
package org.springframework.ide.vscode.parser.spel;
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
import org.antlr.v4.runtime.tree.ParseTreeListener;
/**
* This interface defines a complete listener for a parse tree produced by
* {@link SpelParser}.
*/
public interface SpelParserListener extends ParseTreeListener {
/**
* Enter a parse tree produced by {@link SpelParser#script}.
* @param ctx the parse tree
*/
void enterScript(SpelParser.ScriptContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#script}.
* @param ctx the parse tree
*/
void exitScript(SpelParser.ScriptContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#spelExpr}.
* @param ctx the parse tree
*/
void enterSpelExpr(SpelParser.SpelExprContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#spelExpr}.
* @param ctx the parse tree
*/
void exitSpelExpr(SpelParser.SpelExprContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#node}.
* @param ctx the parse tree
*/
void enterNode(SpelParser.NodeContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#node}.
* @param ctx the parse tree
*/
void exitNode(SpelParser.NodeContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#nonDottedNode}.
* @param ctx the parse tree
*/
void enterNonDottedNode(SpelParser.NonDottedNodeContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#nonDottedNode}.
* @param ctx the parse tree
*/
void exitNonDottedNode(SpelParser.NonDottedNodeContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#dottedNode}.
* @param ctx the parse tree
*/
void enterDottedNode(SpelParser.DottedNodeContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#dottedNode}.
* @param ctx the parse tree
*/
void exitDottedNode(SpelParser.DottedNodeContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#functionOrVar}.
* @param ctx the parse tree
*/
void enterFunctionOrVar(SpelParser.FunctionOrVarContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#functionOrVar}.
* @param ctx the parse tree
*/
void exitFunctionOrVar(SpelParser.FunctionOrVarContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#methodArgs}.
* @param ctx the parse tree
*/
void enterMethodArgs(SpelParser.MethodArgsContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#methodArgs}.
* @param ctx the parse tree
*/
void exitMethodArgs(SpelParser.MethodArgsContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#args}.
* @param ctx the parse tree
*/
void enterArgs(SpelParser.ArgsContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#args}.
* @param ctx the parse tree
*/
void exitArgs(SpelParser.ArgsContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#methodOrProperty}.
* @param ctx the parse tree
*/
void enterMethodOrProperty(SpelParser.MethodOrPropertyContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#methodOrProperty}.
* @param ctx the parse tree
*/
void exitMethodOrProperty(SpelParser.MethodOrPropertyContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#projection}.
* @param ctx the parse tree
*/
void enterProjection(SpelParser.ProjectionContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#projection}.
* @param ctx the parse tree
*/
void exitProjection(SpelParser.ProjectionContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#selection}.
* @param ctx the parse tree
*/
void enterSelection(SpelParser.SelectionContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#selection}.
* @param ctx the parse tree
*/
void exitSelection(SpelParser.SelectionContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#startNode}.
* @param ctx the parse tree
*/
void enterStartNode(SpelParser.StartNodeContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#startNode}.
* @param ctx the parse tree
*/
void exitStartNode(SpelParser.StartNodeContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#literal}.
* @param ctx the parse tree
*/
void enterLiteral(SpelParser.LiteralContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#literal}.
* @param ctx the parse tree
*/
void exitLiteral(SpelParser.LiteralContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#parenspelExpr}.
* @param ctx the parse tree
*/
void enterParenspelExpr(SpelParser.ParenspelExprContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#parenspelExpr}.
* @param ctx the parse tree
*/
void exitParenspelExpr(SpelParser.ParenspelExprContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#typeReference}.
* @param ctx the parse tree
*/
void enterTypeReference(SpelParser.TypeReferenceContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#typeReference}.
* @param ctx the parse tree
*/
void exitTypeReference(SpelParser.TypeReferenceContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#possiblyQualifiedId}.
* @param ctx the parse tree
*/
void enterPossiblyQualifiedId(SpelParser.PossiblyQualifiedIdContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#possiblyQualifiedId}.
* @param ctx the parse tree
*/
void exitPossiblyQualifiedId(SpelParser.PossiblyQualifiedIdContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#nullReference}.
* @param ctx the parse tree
*/
void enterNullReference(SpelParser.NullReferenceContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#nullReference}.
* @param ctx the parse tree
*/
void exitNullReference(SpelParser.NullReferenceContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#constructorReference}.
* @param ctx the parse tree
*/
void enterConstructorReference(SpelParser.ConstructorReferenceContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#constructorReference}.
* @param ctx the parse tree
*/
void exitConstructorReference(SpelParser.ConstructorReferenceContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#constructorArgs}.
* @param ctx the parse tree
*/
void enterConstructorArgs(SpelParser.ConstructorArgsContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#constructorArgs}.
* @param ctx the parse tree
*/
void exitConstructorArgs(SpelParser.ConstructorArgsContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#inlineListOrMap}.
* @param ctx the parse tree
*/
void enterInlineListOrMap(SpelParser.InlineListOrMapContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#inlineListOrMap}.
* @param ctx the parse tree
*/
void exitInlineListOrMap(SpelParser.InlineListOrMapContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#listBindings}.
* @param ctx the parse tree
*/
void enterListBindings(SpelParser.ListBindingsContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#listBindings}.
* @param ctx the parse tree
*/
void exitListBindings(SpelParser.ListBindingsContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#listBinding}.
* @param ctx the parse tree
*/
void enterListBinding(SpelParser.ListBindingContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#listBinding}.
* @param ctx the parse tree
*/
void exitListBinding(SpelParser.ListBindingContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#mapBindings}.
* @param ctx the parse tree
*/
void enterMapBindings(SpelParser.MapBindingsContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#mapBindings}.
* @param ctx the parse tree
*/
void exitMapBindings(SpelParser.MapBindingsContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#mapBinding}.
* @param ctx the parse tree
*/
void enterMapBinding(SpelParser.MapBindingContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#mapBinding}.
* @param ctx the parse tree
*/
void exitMapBinding(SpelParser.MapBindingContext ctx);
/**
* Enter a parse tree produced by {@link SpelParser#beanReference}.
* @param ctx the parse tree
*/
void enterBeanReference(SpelParser.BeanReferenceContext ctx);
/**
* Exit a parse tree produced by {@link SpelParser#beanReference}.
* @param ctx the parse tree
*/
void exitBeanReference(SpelParser.BeanReferenceContext ctx);
}

View File

@@ -34,6 +34,7 @@ import org.springframework.ide.vscode.boot.java.data.jpa.queries.JpqlSupportStat
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveDataProvider;
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
import org.springframework.ide.vscode.boot.maven.PomLanguageServerComponents;
import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider;
@@ -126,7 +127,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
List<LanguageServerComponents> componentsList = List.of(
new BootPropertiesLanguageServerComponents(server, params, javaElementLocationProvider, parser, yamlStructureProvider, yamlAssistContextProvider, sourceLinks),
new BootJavaLanguageServerComponents(appContext),
new SpringXMLLanguageServerComponents(server, springIndexer, params, config),
new SpringXMLLanguageServerComponents(server, springIndexer, params, config, appContext.getBean(SpelReconciler.class)),
new SpringFactoriesLanguageServerComponents(projectFinder, springIndexer, config),
new PomLanguageServerComponents(server, projectFinder, params.projectObserver, appContext.getBean(SpringProjectsProvider.class)),
new JpaQueryPropertiesLanguageServerComponents(server.getTextDocumentService(), projectFinder, appContext.getBean(JpqlSemanticTokens.class),

View File

@@ -22,7 +22,6 @@ import org.springframework.ide.vscode.boot.java.data.jpa.queries.QueryJdtAstReco
import org.springframework.ide.vscode.boot.java.data.jpa.queries.SqlReconciler;
import org.springframework.ide.vscode.boot.java.data.jpa.queries.SqlSemanticTokens;
import org.springframework.ide.vscode.boot.java.reconcilers.AddConfigurationIfBeansPresentReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.AnnotationNodeReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.AuthorizeHttpRequestsReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.AutowiredFieldIntoConstructorParameterReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.BeanMethodNotPublicReconciler;
@@ -41,15 +40,15 @@ import org.springframework.ide.vscode.boot.java.reconcilers.UnnecessarySpringExt
import org.springframework.ide.vscode.boot.java.reconcilers.WebSecurityConfigurerAdapterReconciler;
import org.springframework.ide.vscode.boot.java.semantictokens.EmbeddedLanguagesSemanticTokensSupport;
import org.springframework.ide.vscode.boot.java.semantictokens.JavaSemanticTokensProvider;
import org.springframework.ide.vscode.boot.java.spel.JdtSpelReconciler;
import org.springframework.ide.vscode.boot.java.spel.JdtSpelSemanticTokensProvider;
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
@Configuration(proxyBeanMethods = false)
public class JdtConfig {
@Bean AnnotationNodeReconciler annotationNodeReconciler(BootJavaConfig config) {
return new AnnotationNodeReconciler(config);
}
@Bean BeanMethodNotPublicReconciler beanMethodNotPublicReconciler(SimpleLanguageServer server) {
return new BeanMethodNotPublicReconciler(server.getQuickfixRegistry());
}
@@ -129,9 +128,17 @@ public class JdtConfig {
@Bean QueryJdtAstReconciler dataQueryReconciler(HqlReconciler hqlReconciler, JpqlReconciler jpqlReconciler, SqlReconciler sqlReconciler) {
return new QueryJdtAstReconciler(hqlReconciler, jpqlReconciler, sqlReconciler);
}
@Bean EmbeddedLanguagesSemanticTokensSupport embbededLanguagesSyntaxHighlighting(SimpleLanguageServer server, BootJavaConfig config) {
return new EmbeddedLanguagesSemanticTokensSupport(server, config);
}
@Bean JdtSpelSemanticTokensProvider jdtSpelSemanticTokensProvider(SpelSemanticTokens spelSemanticTokens) {
return new JdtSpelSemanticTokensProvider(spelSemanticTokens);
}
@Bean JdtSpelReconciler jdtSpelReconciler(SpelReconciler spelReconciler) {
return new JdtSpelReconciler(spelReconciler);
}
}

View File

@@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.app;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
@Configuration(proxyBeanMethods = false)
public class SpelConfig {
@Bean SpelSemanticTokens spelSemanticTokens() {
return new SpelSemanticTokens();
}
@Bean SpelReconciler spelReconciler(BootJavaConfig config) {
SpelReconciler reconciler = new SpelReconciler();
config.addListener(v -> reconciler.setEnabled(config.isSpelExpressionValidationEnabled()));
return reconciler;
}
}

View File

@@ -10,6 +10,8 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.app;
import java.util.Optional;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ide.vscode.boot.java.data.jpa.queries.HqlReconciler;
@@ -19,6 +21,8 @@ import org.springframework.ide.vscode.boot.java.data.jpa.queries.JpqlSemanticTok
import org.springframework.ide.vscode.boot.java.data.jpa.queries.JpqlSupportState;
import org.springframework.ide.vscode.boot.java.data.jpa.queries.SqlReconciler;
import org.springframework.ide.vscode.boot.java.data.jpa.queries.SqlSemanticTokens;
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
@@ -26,33 +30,33 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage
public class SpringDataConfig {
@Bean
JpqlSemanticTokens jpqlSemanticTokens() {
return new JpqlSemanticTokens();
JpqlSemanticTokens jpqlSemanticTokens(Optional<SpelSemanticTokens> optSpelTokensProvider) {
return new JpqlSemanticTokens(optSpelTokensProvider);
}
@Bean
HqlSemanticTokens hqlSemanticTokens() {
return new HqlSemanticTokens();
HqlSemanticTokens hqlSemanticTokens(Optional<SpelSemanticTokens> optSpelTokensProvider) {
return new HqlSemanticTokens(optSpelTokensProvider);
}
@Bean
SqlSemanticTokens sqlSemanticTokens() {
return new SqlSemanticTokens();
SqlSemanticTokens sqlSemanticTokens(Optional<SpelSemanticTokens> optSpelTokensProvider) {
return new SqlSemanticTokens(optSpelTokensProvider);
}
@Bean
HqlReconciler hqlReconciler() {
return new HqlReconciler();
HqlReconciler hqlReconciler(Optional<SpelReconciler> spelReconciler) {
return new HqlReconciler(spelReconciler);
}
@Bean
JpqlReconciler jpqlReconciler() {
return new JpqlReconciler();
JpqlReconciler jpqlReconciler(Optional<SpelReconciler> spelReconciler) {
return new JpqlReconciler(spelReconciler);
}
@Bean
SqlReconciler sqlReconciler() {
return new SqlReconciler();
SqlReconciler sqlReconciler(Optional<SpelReconciler> spelReconciler) {
return new SqlReconciler(spelReconciler);
}
@Bean

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.boot.java.data.jpa.queries;
import java.util.BitSet;
import java.util.Optional;
import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.CharStreams;
@@ -22,13 +23,24 @@ 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.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.ide.vscode.boot.java.handlers.Reconciler;
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
import org.springframework.ide.vscode.parser.hql.HqlBaseListener;
import org.springframework.ide.vscode.parser.hql.HqlLexer;
import org.springframework.ide.vscode.parser.hql.HqlParser;
import org.springframework.ide.vscode.parser.jpql.JpqlParser;
public class HqlReconciler implements Reconciler {
private final Optional<SpelReconciler> spelReconciler;
public HqlReconciler(Optional<SpelReconciler> spelReconiler) {
this.spelReconciler = spelReconiler;
}
@Override
public void reconcile(String text, int startPosition, IProblemCollector problemCollector) {
@@ -36,6 +48,7 @@ public class HqlReconciler implements Reconciler {
CommonTokenStream antlrTokens = new CommonTokenStream(lexer);
HqlParser parser = new HqlParser(antlrTokens);
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.addErrorListener(new ANTLRErrorListener() {
@@ -69,6 +82,27 @@ public class HqlReconciler implements Reconciler {
}
});
// Reconcile embedded SPEL
parser.addParseListener(new HqlBaseListener() {
private void processTerminal(TerminalNode node) {
if (node.getSymbol().getType() == JpqlParser.SPEL) {
spelReconciler.ifPresent(r -> JpqlReconciler.reconcileEmbeddedSpelNode(node, startPosition, r, problemCollector));
}
}
@Override
public void visitTerminal(TerminalNode node) {
processTerminal(node);
}
@Override
public void visitErrorNode(ErrorNode node) {
processTerminal(node);
}
});
parser.ql_statement();
}

View File

@@ -10,11 +10,13 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.data.jpa.queries;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Optional;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
@@ -22,6 +24,7 @@ import org.antlr.v4.runtime.ConsoleErrorListener;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensDataProvider;
import org.springframework.ide.vscode.parser.hql.HqlBaseListener;
@@ -35,21 +38,36 @@ import org.springframework.ide.vscode.parser.hql.HqlParser.SimplePathElementCont
public class HqlSemanticTokens implements SemanticTokensDataProvider {
private static List<String> TOKEN_TYPES = List.of("keyword", "type", "class", "string", "number", "operator",
"variable", "method", "parameter", "regexp");
"variable", "method", "parameter");
private Optional<SpelSemanticTokens> optSpelTokens;
public HqlSemanticTokens(Optional<SpelSemanticTokens> optSpelTokens) {
this.optSpelTokens = optSpelTokens;
}
@Override
public List<String> getTokenTypes() {
return TOKEN_TYPES;
LinkedHashSet<String> tokenTypes = new LinkedHashSet<>(TOKEN_TYPES);
tokenTypes.addAll(optSpelTokens.map(s -> s.getTokenTypes()).orElse(Collections.emptyList()));
return tokenTypes.stream().toList();
}
@Override
public List<String> getTypeModifiers() {
return optSpelTokens.map(s -> s.getTypeModifiers()).orElse(Collections.emptyList());
}
@Override
public List<SemanticTokenData> computeTokens(String text, int initialOffset) {
HqlLexer lexer = new HqlLexer(CharStreams.fromString(text));
CommonTokenStream antlrTokens = new CommonTokenStream(lexer);
HqlParser parser = new HqlParser(antlrTokens);
HqlParser parser = new HqlParser(antlrTokens);
Map<Token, String> semantics = new HashMap<>();
List<SemanticTokenData> tokens = new ArrayList<>();
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.addParseListener(new HqlBaseListener() {
@@ -76,7 +94,7 @@ public class HqlSemanticTokens implements SemanticTokensDataProvider {
case HqlParser.WS:
break;
case HqlParser.SPEL:
semantics.put(node.getSymbol(), "regexp");
tokens.addAll(JpqlSemanticTokens.computeTokensFromSpelNode(node, initialOffset, optSpelTokens));
break;
default:
if (HqlParser.WS < type && type <= HqlParser.YEAR) {
@@ -120,11 +138,11 @@ public class HqlSemanticTokens implements SemanticTokensDataProvider {
parser.ql_statement();
List<SemanticTokenData> tokens = semantics.entrySet().stream()
semantics.entrySet().stream()
.map(e -> new SemanticTokenData(e.getKey().getStartIndex() + initialOffset,
e.getKey().getStartIndex() + e.getKey().getText().length() + initialOffset, e.getValue(),
new String[0]))
.collect(Collectors.toList());
.forEach(tokens::add);
Collections.sort(tokens);

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.boot.java.data.jpa.queries;
import java.util.BitSet;
import java.util.Optional;
import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.CharStreams;
@@ -22,20 +23,31 @@ 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.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.ide.vscode.boot.java.handlers.Reconciler;
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
import org.springframework.ide.vscode.parser.jpql.JpqlBaseListener;
import org.springframework.ide.vscode.parser.jpql.JpqlLexer;
import org.springframework.ide.vscode.parser.jpql.JpqlParser;
public class JpqlReconciler implements Reconciler {
private final Optional<SpelReconciler> spelReconciler;
public JpqlReconciler(Optional<SpelReconciler> spelReconciler) {
this.spelReconciler = spelReconciler;
}
@Override
public void reconcile(String text, int startPosition, IProblemCollector problemCollector) {
JpqlLexer lexer = new JpqlLexer(CharStreams.fromString(text));
CommonTokenStream antlrTokens = new CommonTokenStream(lexer);
JpqlParser parser = new JpqlParser(antlrTokens);
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.addErrorListener(new ANTLRErrorListener() {
@@ -69,8 +81,36 @@ public class JpqlReconciler implements Reconciler {
}
});
// Reconcile embedded SPEL
parser.addParseListener(new JpqlBaseListener() {
private void processTerminal(TerminalNode node) {
if (node.getSymbol().getType() == JpqlParser.SPEL) {
spelReconciler.ifPresent(r -> reconcileEmbeddedSpelNode(node, startPosition, r, problemCollector));
}
}
@Override
public void visitTerminal(TerminalNode node) {
processTerminal(node);
}
@Override
public void visitErrorNode(ErrorNode node) {
processTerminal(node);
}
});
parser.ql_statement();
}
static void reconcileEmbeddedSpelNode(TerminalNode node, int initialOffset, SpelReconciler spelReconciler, IProblemCollector problemCollector) {
int startPosition = initialOffset + node.getSymbol().getStartIndex();
String spelContent = node.getSymbol().getText().substring(2, node.getSymbol().getText().length() - 1);
spelReconciler.reconcile(spelContent, startPosition, problemCollector);
}
}

View File

@@ -10,11 +10,13 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.data.jpa.queries;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Optional;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
@@ -22,6 +24,7 @@ import org.antlr.v4.runtime.ConsoleErrorListener;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensDataProvider;
import org.springframework.ide.vscode.parser.jpql.JpqlBaseListener;
@@ -39,19 +42,34 @@ public class JpqlSemanticTokens implements SemanticTokensDataProvider {
private static List<String> TOKEN_TYPES = List.of("keyword", "type", "class", "string", "number", "operator",
"variable", "method", "regexp", "parameter");
final private Optional<SpelSemanticTokens> optSpelSemantictokens;
public JpqlSemanticTokens(Optional<SpelSemanticTokens> optSpelSemantictokens) {
this.optSpelSemantictokens = optSpelSemantictokens;
}
@Override
public List<String> getTokenTypes() {
return TOKEN_TYPES;
LinkedHashSet<String> tokenTypes = new LinkedHashSet<>(TOKEN_TYPES);
tokenTypes.addAll(optSpelSemantictokens.map(s -> s.getTokenTypes()).orElse(Collections.emptyList()));
return tokenTypes.stream().toList();
}
@Override
public List<String> getTypeModifiers() {
return optSpelSemantictokens.map(s -> s.getTypeModifiers()).orElse(Collections.emptyList());
}
@Override
public List<SemanticTokenData> computeTokens(String text, int initialOffset) {
JpqlLexer lexer = new JpqlLexer(CharStreams.fromString(text));
CommonTokenStream antlrTokens = new CommonTokenStream(lexer);
JpqlParser parser = new JpqlParser(antlrTokens);
Map<Token, String> semantics = new HashMap<>();
List<SemanticTokenData> tokens = new ArrayList<>();
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.addParseListener(new JpqlBaseListener() {
@@ -81,7 +99,7 @@ public class JpqlSemanticTokens implements SemanticTokensDataProvider {
case JpqlParser.WS:
break;
case JpqlParser.SPEL:
semantics.put(node.getSymbol(), "regexp");
tokens.addAll(computeTokensFromSpelNode(node, initialOffset, optSpelSemantictokens));
break;
default:
if (JpqlParser.WS < type && type <= JpqlParser.WHERE) {
@@ -140,15 +158,35 @@ public class JpqlSemanticTokens implements SemanticTokensDataProvider {
parser.ql_statement();
List<SemanticTokenData> tokens = semantics.entrySet().stream()
semantics.entrySet().stream()
.map(e -> new SemanticTokenData(e.getKey().getStartIndex() + initialOffset,
e.getKey().getStartIndex() + e.getKey().getText().length() + initialOffset, e.getValue(),
new String[0]))
.collect(Collectors.toList());
.forEach(tokens::add);
Collections.sort(tokens);
return tokens;
}
static List<SemanticTokenData> computeTokensFromSpelNode(TerminalNode node, int initialOffset, Optional<SpelSemanticTokens> optSpelTokens) {
List<SemanticTokenData> spelTokens = new ArrayList<>();
int startPosition = initialOffset + node.getSymbol().getStartIndex();
int spelStartPosition = startPosition + 2;
int endPosition = startPosition + node.getText().length();
int spelEndPosition = endPosition - 1;
// '#{' operator
spelTokens.add(new SemanticTokenData(startPosition, spelStartPosition, "operator", new String[0]));
// SPEL contents
optSpelTokens.ifPresentOrElse(
spelTokenProvider -> spelTokens
.addAll(spelTokenProvider.computeTokens(node.getText().substring(2, node.getText().length() - 1), spelStartPosition)),
() -> spelTokens.add(new SemanticTokenData(spelStartPosition, spelEndPosition, "string", new String[0])));
// '}' operator
spelTokens.add(new SemanticTokenData(spelEndPosition, endPosition, "operator", new String[0]));
return spelTokens;
}
}

View File

@@ -39,7 +39,7 @@ public class NamedQueryPropertiesReconcileEngine implements IReconcileEngine {
try {
Reconciler reconciler = projectFinder.find(new TextDocumentIdentifier(doc.getUri()))
.map(p -> SpringProjectUtil.hasDependencyStartingWith(p, "spring-data-jpa", null) ? hqlReconciler : jpqlReconciler)
.orElse(new JpqlReconciler());
.orElse(jpqlReconciler);
AntlrParser parser = new AntlrParser();
ParseResults parseResults = parser.parse(doc.get());

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.boot.java.data.jpa.queries;
import java.util.BitSet;
import java.util.Optional;
import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.CharStreams;
@@ -22,20 +23,31 @@ 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.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.ide.vscode.boot.java.handlers.Reconciler;
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
import org.springframework.ide.vscode.parser.sql.MySqlLexer;
import org.springframework.ide.vscode.parser.sql.MySqlParser;
import org.springframework.ide.vscode.parser.sql.MySqlParserBaseListener;
public class SqlReconciler implements Reconciler {
private final Optional<SpelReconciler> spelReconciler;
public SqlReconciler(Optional<SpelReconciler> spelReconciler) {
this.spelReconciler = spelReconciler;
}
@Override
public void reconcile(String text, int startPosition, IProblemCollector problemCollector) {
MySqlLexer lexer = new MySqlLexer(CharStreams.fromString(text));
CommonTokenStream antlrTokens = new CommonTokenStream(lexer);
MySqlParser parser = new MySqlParser(antlrTokens);
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.addErrorListener(new ANTLRErrorListener() {
@@ -69,6 +81,28 @@ public class SqlReconciler implements Reconciler {
}
});
// Reconcile embedded SPEL
parser.addParseListener(new MySqlParserBaseListener() {
private void processTerminal(TerminalNode node) {
if (node.getSymbol().getType() == MySqlParser.SPEL) {
spelReconciler.ifPresent(r -> JpqlReconciler.reconcileEmbeddedSpelNode(node, startPosition, r, problemCollector));
}
}
@Override
public void visitTerminal(TerminalNode node) {
processTerminal(node);
}
@Override
public void visitErrorNode(ErrorNode node) {
processTerminal(node);
}
});
parser.sqlStatements();
}

View File

@@ -10,18 +10,28 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.data.jpa.queries;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Optional;
import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ConsoleErrorListener;
import org.antlr.v4.runtime.Parser;
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.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensDataProvider;
import org.springframework.ide.vscode.parser.sql.MySqlLexer;
@@ -33,11 +43,23 @@ public class SqlSemanticTokens implements SemanticTokensDataProvider {
private static List<String> TOKEN_TYPES = List.of("keyword", "type", "string", "number", "operator",
"variable", "regexp", "comment");
@Override
public List<String> getTokenTypes() {
return TOKEN_TYPES;
private Optional<SpelSemanticTokens> optSpelTokens;
public SqlSemanticTokens(Optional<SpelSemanticTokens> optSpelTokens) {
this.optSpelTokens = optSpelTokens;
}
@Override
public List<String> getTokenTypes() {
LinkedHashSet<String> tokenTypes = new LinkedHashSet<>(TOKEN_TYPES);
tokenTypes.addAll(optSpelTokens.map(s -> s.getTokenTypes()).orElse(Collections.emptyList()));
return tokenTypes.stream().toList();
}
@Override
public List<String> getTypeModifiers() {
return optSpelTokens.map(s -> s.getTypeModifiers()).orElse(Collections.emptyList());
}
@Override
public List<SemanticTokenData> computeTokens(String text, int initialOffset) {
@@ -47,6 +69,9 @@ public class SqlSemanticTokens implements SemanticTokensDataProvider {
Map<Token, String> semantics = new HashMap<>();
List<SemanticTokenData> tokens = new ArrayList<>();
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.addParseListener(new MySqlParserBaseListener() {
@@ -55,7 +80,7 @@ public class SqlSemanticTokens implements SemanticTokensDataProvider {
int type = node.getSymbol().getType();
switch (type) {
case MySqlParser.SPEL:
semantics.put(node.getSymbol(), "regexp");
tokens.addAll(JpqlSemanticTokens.computeTokensFromSpelNode(node, initialOffset, optSpelTokens));
break;
case MySqlParser.ID:
semantics.put(node.getSymbol(), "variable");
@@ -162,13 +187,44 @@ public class SqlSemanticTokens implements SemanticTokensDataProvider {
});
parser.addErrorListener(new ANTLRErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
String msg, RecognitionException e) {
// TODO Auto-generated method stub
msg.length();
}
@Override
public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction,
ATNConfigSet configs) {
// TODO Auto-generated method stub
}
@Override
public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex,
BitSet conflictingAlts, ATNConfigSet configs) {
// TODO Auto-generated method stub
}
@Override
public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact,
BitSet ambigAlts, ATNConfigSet configs) {
// TODO Auto-generated method stub
}
});
parser.sqlStatements();
List<SemanticTokenData> tokens = semantics.entrySet().stream()
semantics.entrySet().stream()
.map(e -> new SemanticTokenData(e.getKey().getStartIndex() + initialOffset,
e.getKey().getStartIndex() + e.getKey().getText().length() + initialOffset, e.getValue(),
new String[0]))
.collect(Collectors.toList());
.forEach(tokens::add);
Collections.sort(tokens);

View File

@@ -1,136 +0,0 @@
/*******************************************************************************
* Copyright (c) 2023, 2024 VMware, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.reconcilers;
import java.net.URI;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.SpelProblemType;
import org.springframework.ide.vscode.boot.java.handlers.SpelExpressionReconciler;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
public class AnnotationNodeReconciler implements JdtAstReconciler {
public static final String SPRING_CACHEABLE = "org.springframework.cache.annotation.Cacheable";
public static final String SPRING_CACHE_EVICT = "org.springframework.cache.annotation.CacheEvict";
public static final String SPRING_EVENT_LISTENER = "org.springframework.context.event.EventListener";
public static final String SPRING_PRE_AUTHORIZE = "org.springframework.security.access.prepost.PreAuthorize";
public static final String SPRING_PRE_FILTER = "org.springframework.security.access.prepost.PreFilter";
public static final String SPRING_POST_AUTHORIZE = "org.springframework.security.access.prepost.PostAuthorize";
public static final String SPRING_POST_FILTER= "org.springframework.security.access.prepost.PostFilter";
public static final String SPRING_CONDITIONAL_ON_EXPRESSION = "org.springframework.boot.autoconfigure.condition.ConditionalOnExpression";
private final SpelExpressionReconciler spelExpressionReconciler;
private final AnnotationReconciler[] reconcilers;
public AnnotationNodeReconciler(BootJavaConfig config) {
this.spelExpressionReconciler = new SpelExpressionReconciler();
this.reconcilers = new AnnotationReconciler[] {
new AnnotationParamReconciler(Annotations.VALUE, null, "#{", "}", spelExpressionReconciler),
new AnnotationParamReconciler(Annotations.VALUE, "value", "#{", "}", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_CACHEABLE, "key", "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_CACHEABLE, "condition", "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_CACHEABLE, "unless", "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_CACHE_EVICT, "key", "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_CACHE_EVICT, "condition", "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_EVENT_LISTENER, "condition", "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_PRE_AUTHORIZE, null, "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_PRE_AUTHORIZE, "value", "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_PRE_FILTER, null, "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_PRE_FILTER, "value", "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_POST_AUTHORIZE, null, "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_POST_AUTHORIZE, "value", "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_POST_FILTER, null, "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_POST_FILTER, "value", "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_CONDITIONAL_ON_EXPRESSION, null, "", "", spelExpressionReconciler),
new AnnotationParamReconciler(SPRING_CONDITIONAL_ON_EXPRESSION, "value", "", "", spelExpressionReconciler)
};
config.addListener(evt -> this.spelExpressionReconciler.setEnabled(config.isSpelExpressionValidationEnabled()));
}
@Override
public boolean isApplicable(IJavaProject project) {
return true;
}
@Override
public ProblemType getProblemType() {
return SpelProblemType.JAVA_SPEL_EXPRESSION_SYNTAX;
}
@Override
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu,
IProblemCollector problemCollector, boolean isCompleteAst) {
return new ASTVisitor() {
@Override
public boolean visit(SingleMemberAnnotation node) {
try {
visitAnnotation(project, docUri, node, problemCollector);
}
catch (Exception e) {
}
return super.visit(node);
}
@Override
public boolean visit(NormalAnnotation node) {
try {
visitAnnotation(project, docUri, node, problemCollector);
}
catch (Exception e) {
}
return super.visit(node);
}
@Override
public boolean visit(MarkerAnnotation node) {
try {
visitAnnotation(project, docUri, node, problemCollector);
}
catch (Exception e) {
}
return super.visit(node);
}
};
}
private void visitAnnotation(IJavaProject project, URI docUri, Annotation node, IProblemCollector problemCollector) {
ITypeBinding typeBinding = node.resolveTypeBinding();
if (typeBinding != null) {
for (int i = 0; i < reconcilers.length; i++) {
reconcilers[i].visit(project, docUri, node, typeBinding, problemCollector);
}
}
}
}

View File

@@ -1,107 +0,0 @@
/*******************************************************************************
* Copyright (c) 2020, 2022 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.reconcilers;
import java.net.URI;
import java.util.List;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
import org.springframework.ide.vscode.boot.java.handlers.Reconciler;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
/**
* @author Martin Lippert
*/
public class AnnotationParamReconciler implements AnnotationReconciler {
private final String annotationType;
private final String paramName;
private final String paramValuePrefix;
private final String paramValuePostfix;
private final Reconciler reconciler;
public AnnotationParamReconciler(String annotationType, String paramName,
String paramValuePrefix, String paramValuePostfix, Reconciler reconciler) {
this.annotationType = annotationType;
this.paramName = paramName;
this.paramValuePrefix = paramValuePrefix;
this.paramValuePostfix = paramValuePostfix;
this.reconciler = reconciler;
}
public void visit(IJavaProject project, URI docUri, Annotation node, ITypeBinding typeBinding, IProblemCollector problemCollector) {
if (node instanceof SingleMemberAnnotation) {
visitSingleMemberAnnotation((SingleMemberAnnotation) node, typeBinding, problemCollector);
} else if (node instanceof NormalAnnotation) {
visitNormalAnnotation((NormalAnnotation) node, typeBinding, problemCollector);
}
}
protected void visitSingleMemberAnnotation(SingleMemberAnnotation node, ITypeBinding typeBinding, IProblemCollector problemCollector) {
if (this.paramName != null) {
return;
}
if (!AnnotationHierarchies.hasTransitiveSuperAnnotationType(typeBinding, this.annotationType)) {
return;
}
Expression valueExp = node.getValue();
if (valueExp instanceof StringLiteral) {
reconcileStringLiteral((StringLiteral) valueExp, problemCollector);
}
}
protected void visitNormalAnnotation(NormalAnnotation node, ITypeBinding typeBinding, IProblemCollector problemCollector) {
if (paramName == null) {
return;
}
if (!AnnotationHierarchies.hasTransitiveSuperAnnotationType(typeBinding, this.annotationType)) {
return;
}
List<?> values = node.values();
for (Object value : values) {
if (value instanceof MemberValuePair) {
MemberValuePair pair = (MemberValuePair) value;
String name = pair.getName().getFullyQualifiedName();
if (name != null && name.equals(paramName)) {
Expression expression = pair.getValue();
if (expression instanceof StringLiteral) {
reconcileStringLiteral((StringLiteral) expression, problemCollector);
}
}
}
}
}
private void reconcileStringLiteral(StringLiteral valueExp, IProblemCollector problemCollector) {
String value = valueExp.getLiteralValue();
if (value != null && value.startsWith(paramValuePrefix) && value.endsWith(paramValuePostfix)) {
String valueToReconcile = value.substring(paramValuePrefix.length(), value.length() - paramValuePostfix.length());
reconciler.reconcile(valueToReconcile, valueExp.getStartPosition() + paramValuePrefix.length() + 1, problemCollector);
}
}
}

View File

@@ -1,24 +0,0 @@
/*******************************************************************************
* Copyright (c) 2022 VMware, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.reconcilers;
import java.net.URI;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
public interface AnnotationReconciler {
void visit(IJavaProject project, URI docUri, Annotation node, ITypeBinding typeBinding, IProblemCollector problemCollector);
}

View File

@@ -0,0 +1,137 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.spel;
import java.util.List;
import java.util.Optional;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
final class AnnotationParamSpelExtractor {
private static final String SPRING_CACHEABLE = "org.springframework.cache.annotation.Cacheable";
private static final String SPRING_CACHE_EVICT = "org.springframework.cache.annotation.CacheEvict";
private static final String SPRING_EVENT_LISTENER = "org.springframework.context.event.EventListener";
private static final String SPRING_PRE_AUTHORIZE = "org.springframework.security.access.prepost.PreAuthorize";
private static final String SPRING_PRE_FILTER = "org.springframework.security.access.prepost.PreFilter";
private static final String SPRING_POST_AUTHORIZE = "org.springframework.security.access.prepost.PostAuthorize";
private static final String SPRING_POST_FILTER= "org.springframework.security.access.prepost.PostFilter";
private static final String SPRING_CONDITIONAL_ON_EXPRESSION = "org.springframework.boot.autoconfigure.condition.ConditionalOnExpression";
final static AnnotationParamSpelExtractor[] SPEL_EXTRACTORS = new AnnotationParamSpelExtractor[] {
new AnnotationParamSpelExtractor(Annotations.VALUE, null, "#{", "}"),
new AnnotationParamSpelExtractor(Annotations.VALUE, "value", "#{", "}"),
new AnnotationParamSpelExtractor(SPRING_CACHEABLE, "key", "", ""),
new AnnotationParamSpelExtractor(SPRING_CACHEABLE, "condition", "", ""),
new AnnotationParamSpelExtractor(SPRING_CACHEABLE, "unless", "", ""),
new AnnotationParamSpelExtractor(SPRING_CACHE_EVICT, "key", "", ""),
new AnnotationParamSpelExtractor(SPRING_CACHE_EVICT, "condition", "", ""),
new AnnotationParamSpelExtractor(SPRING_EVENT_LISTENER, "condition", "", ""),
new AnnotationParamSpelExtractor(SPRING_PRE_AUTHORIZE, null, "", ""),
new AnnotationParamSpelExtractor(SPRING_PRE_AUTHORIZE, "value", "", ""),
new AnnotationParamSpelExtractor(SPRING_PRE_FILTER, null, "", ""),
new AnnotationParamSpelExtractor(SPRING_PRE_FILTER, "value", "", ""),
new AnnotationParamSpelExtractor(SPRING_POST_AUTHORIZE, null, "", ""),
new AnnotationParamSpelExtractor(SPRING_POST_AUTHORIZE, "value", "", ""),
new AnnotationParamSpelExtractor(SPRING_POST_FILTER, null, "", ""),
new AnnotationParamSpelExtractor(SPRING_POST_FILTER, "value", "", ""),
new AnnotationParamSpelExtractor(SPRING_CONDITIONAL_ON_EXPRESSION, null, "", ""),
new AnnotationParamSpelExtractor(SPRING_CONDITIONAL_ON_EXPRESSION, "value", "", "")
};
record Snippet(String text, int offset) {}
private final String annotationType;
private final String paramName;
private final String paramValuePrefix;
private final String paramValuePostfix;
public AnnotationParamSpelExtractor(String annotationType, String paramName, String paramValuePrefix,
String paramValuePostfix) {
this.annotationType = annotationType;
this.paramName = paramName;
this.paramValuePrefix = paramValuePrefix;
this.paramValuePostfix = paramValuePostfix;
}
Optional<Snippet> getSpelRegion(NormalAnnotation a) {
if (paramName == null) {
return Optional.empty();
}
if (!AnnotationHierarchies.hasTransitiveSuperAnnotationType(a.resolveTypeBinding(), this.annotationType)) {
return Optional.empty();
}
List<?> values = a.values();
for (Object value : values) {
if (value instanceof MemberValuePair) {
MemberValuePair pair = (MemberValuePair) value;
String name = pair.getName().getFullyQualifiedName();
if (name != null && name.equals(paramName)) {
Expression expression = pair.getValue();
if (expression instanceof StringLiteral) {
return fromStringLiteral((StringLiteral) expression);
}
}
}
}
return Optional.empty();
}
Optional<Snippet> getSpelRegion(SingleMemberAnnotation a) {
if (this.paramName != null) {
return Optional.empty();
}
if (!AnnotationHierarchies.hasTransitiveSuperAnnotationType(a.resolveTypeBinding(), this.annotationType)) {
return Optional.empty();
}
Expression valueExp = a.getValue();
if (valueExp instanceof StringLiteral) {
return fromStringLiteral((StringLiteral) valueExp);
}
return Optional.empty();
}
private Optional<Snippet> fromStringLiteral(StringLiteral valueExp) {
String value = valueExp.getEscapedValue();
value = value.substring(1, value.length() - 1);
if (value != null && value.startsWith(paramValuePrefix) && value.endsWith(paramValuePostfix)) {
String spelText = value.substring(paramValuePrefix.length(), value.length() - paramValuePostfix.length());
int offset = valueExp.getStartPosition() + paramValuePrefix.length() + 1;
return Optional.of(new Snippet(spelText, offset));
}
return Optional.empty();
}
}

View File

@@ -0,0 +1,73 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.spel;
import java.net.URI;
import java.util.Arrays;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.springframework.ide.vscode.boot.java.SpelProblemType;
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
public class JdtSpelReconciler implements JdtAstReconciler {
final private AnnotationParamSpelExtractor[] spelExtractors = AnnotationParamSpelExtractor.SPEL_EXTRACTORS;
final private SpelReconciler spelReconciler;
public JdtSpelReconciler(SpelReconciler spelReconciler) {
this.spelReconciler = spelReconciler;
}
@Override
public boolean isApplicable(IJavaProject project) {
return SpringProjectUtil.hasDependencyStartingWith(project, "spring-expression", null);
}
@Override
public ProblemType getProblemType() {
return SpelProblemType.JAVA_SPEL_EXPRESSION_SYNTAX;
}
@Override
public ASTVisitor createVisitor(IJavaProject project, URI docURI, CompilationUnit cu,
IProblemCollector problemCollector, boolean isCompleteAst) {
return new ASTVisitor() {
@Override
public boolean visit(SingleMemberAnnotation node) {
Arrays.stream(spelExtractors)
.map(e -> e.getSpelRegion(node))
.filter(o -> o.isPresent())
.map(o -> o.get())
.forEach(snippet -> spelReconciler.reconcile(snippet.text(), snippet.offset(), problemCollector));
return super.visit(node);
}
@Override
public boolean visit(NormalAnnotation node) {
Arrays.stream(spelExtractors)
.map(e -> e.getSpelRegion(node))
.filter(o -> o.isPresent())
.map(o -> o.get())
.forEach(snippet -> spelReconciler.reconcile(snippet.text(), snippet.offset(), problemCollector));
return super.visit(node);
}
};
}
}

View File

@@ -0,0 +1,80 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.spel;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.springframework.ide.vscode.boot.java.JdtSemanticTokensProvider;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
import org.springframework.ide.vscode.commons.util.Collector;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
public class JdtSpelSemanticTokensProvider implements JdtSemanticTokensProvider {
final private AnnotationParamSpelExtractor[] spelExtractors = AnnotationParamSpelExtractor.SPEL_EXTRACTORS;
final private SpelSemanticTokens tokensProvider;
public JdtSpelSemanticTokensProvider(SpelSemanticTokens tokensProvider) {
this.tokensProvider = tokensProvider;
}
@Override
public List<String> getTokenTypes() {
return tokensProvider.getTokenTypes();
}
@Override
public List<String> getTokenModifiers() {
return tokensProvider.getTypeModifiers();
}
@Override
public boolean isApplicable(IJavaProject project) {
return SpringProjectUtil.hasDependencyStartingWith(project, "spring-expression", null);
}
@Override
public ASTVisitor getTokensComputer(IJavaProject project, TextDocument doc, CompilationUnit cu,
Collector<SemanticTokenData> collector) {
return new ASTVisitor() {
@Override
public boolean visit(SingleMemberAnnotation node) {
Arrays.stream(spelExtractors)
.map(e -> e.getSpelRegion(node))
.filter(o -> o.isPresent())
.map(o -> o.get())
.forEach(snippet -> tokensProvider.computeTokens(snippet.text(), snippet.offset()).forEach(collector::accept));
return super.visit(node);
}
@Override
public boolean visit(NormalAnnotation node) {
Arrays.stream(spelExtractors)
.map(e -> e.getSpelRegion(node))
.filter(o -> o.isPresent())
.map(o -> o.get())
.forEach(snippet -> tokensProvider.computeTokens(snippet.text(), snippet.offset()).forEach(collector::accept));
return super.visit(node);
}
};
}
}

View File

@@ -0,0 +1,86 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.spel;
import java.util.BitSet;
import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ConsoleErrorListener;
import org.antlr.v4.runtime.Parser;
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.boot.java.SpelProblemType;
import org.springframework.ide.vscode.boot.java.handlers.Reconciler;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
import org.springframework.ide.vscode.parser.spel.SpelLexer;
import org.springframework.ide.vscode.parser.spel.SpelParser;
public class SpelReconciler implements Reconciler {
private boolean enabled = true;
public void setEnabled(boolean spelExpressionValidationEnabled) {
this.enabled = spelExpressionValidationEnabled;
}
@Override
public void reconcile(String text, int startPosition, IProblemCollector problemCollector) {
if (!enabled) {
return;
}
SpelLexer lexer = new SpelLexer(CharStreams.fromString(text));
CommonTokenStream antlrTokens = new CommonTokenStream(lexer);
SpelParser parser = new SpelParser(antlrTokens);
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.addErrorListener(new ANTLRErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
String msg, RecognitionException e) {
Token token = (Token) offendingSymbol;
int offset = token.getStartIndex();
int length = token.getStopIndex() - token.getStartIndex() + 1;
if (token.getStartIndex() >= token.getStopIndex()) {
offset = token.getStartIndex() - token.getCharPositionInLine();
length = token.getCharPositionInLine() + 1;
}
problemCollector.accept(new ReconcileProblemImpl(SpelProblemType.JAVA_SPEL_EXPRESSION_SYNTAX, "SPEL: " + msg, startPosition + offset, length));
}
@Override
public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction,
ATNConfigSet configs) {
}
@Override
public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex,
BitSet conflictingAlts, ATNConfigSet configs) {
}
@Override
public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact,
BitSet ambigAlts, ATNConfigSet configs) {
}
});
parser.spelExpr();
}
}

View File

@@ -0,0 +1,206 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.spel;
import static org.springframework.ide.vscode.parser.spel.SpelLexer.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ConsoleErrorListener;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensDataProvider;
import org.springframework.ide.vscode.parser.spel.SpelLexer;
import org.springframework.ide.vscode.parser.spel.SpelParser;
import org.springframework.ide.vscode.parser.spel.SpelParser.BeanReferenceContext;
import org.springframework.ide.vscode.parser.spel.SpelParser.ConstructorReferenceContext;
import org.springframework.ide.vscode.parser.spel.SpelParser.DottedNodeContext;
import org.springframework.ide.vscode.parser.spel.SpelParser.FunctionOrVarContext;
import org.springframework.ide.vscode.parser.spel.SpelParser.MethodOrPropertyContext;
import org.springframework.ide.vscode.parser.spel.SpelParser.PossiblyQualifiedIdContext;
import org.springframework.ide.vscode.parser.spel.SpelParserBaseListener;
public class SpelSemanticTokens implements SemanticTokensDataProvider {
@Override
public List<String> getTokenTypes() {
return List.of("operator", "keyword", "type", "string", "number", "method", "property");
}
@Override
public List<SemanticTokenData> computeTokens(String text, int initialOffset) {
SpelLexer lexer = new SpelLexer(CharStreams.fromString(text));
CommonTokenStream antlrTokens = new CommonTokenStream(lexer);
SpelParser parser = new SpelParser(antlrTokens);
Map<Token, String> semantics = new HashMap<>();
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
parser.addParseListener(new SpelParserBaseListener() {
private void processTerminalNode(TerminalNode node) {
int type = node.getSymbol().getType();
switch (type) {
case INC:
case PLUS:
case DEC:
case MINUS:
case DOT:
case COMMA:
case STAR:
case DIV:
case MOD:
case LPAREN:
case RPAREN:
case LSQUARE:
case RSQUARE:
case POWER:
case ELVIS:
case NE:
case NOT:
case EQ:
case ASSIGN:
case SYMBOLIC_AND:
case SYMBOLIC_OR:
case GE:
case LE:
case GT:
case LT:
case QMARK:
case RCURLY:
case LCURLY:
case SEMICOLON:
case COLON:
case SELECT_FIRST:
case HASH:
case BEAN_REF:
case PROJECT:
case FACTORY_BEAN_REF:
case SELECT:
case SAFE_NAVI:
case SELECT_LAST:
case BACKTICK:
semantics.put(node.getSymbol(), "operator");
break;
case OR:
case AND:
case NEW:
case NULL:
case TRUE:
case FALSE:
case T:
case MATCHES:
case LE_KEYWORD:
case LT_KEYWORD:
case GE_KEYWORD:
case GT_KEYWORD:
case EQ_KEYWORD:
case NE_KEYWORD:
semantics.put(node.getSymbol(), "keyword");
break;
case IDENTIFIER:
semantics.put(node.getSymbol(), "variable");
break;
case NUMERIC_LITERAL:
case INTEGER_LITERAL:
case REAL_LITERAL:
semantics.put(node.getSymbol(), "number");
break;
case STRING_LITERAL:
case SINGLE_QUOTED_STRING:
case DOUBLE_QUOTED_STRING:
semantics.put(node.getSymbol(), "string");
break;
}
}
@Override
public void exitBeanReference(BeanReferenceContext ctx) {
if (ctx.IDENTIFIER() != null) {
semantics.put(ctx.IDENTIFIER().getSymbol(), "type");
}
if (ctx.STRING_LITERAL() != null) {
semantics.put(ctx.STRING_LITERAL().getSymbol(), "type");
}
}
@Override
public void exitMethodOrProperty(MethodOrPropertyContext ctx) {
if (ctx.methodArgs() != null) {
semantics.put(ctx.IDENTIFIER().getSymbol(), "method");
} else {
if (ctx.parent instanceof DottedNodeContext) {
semantics.put(ctx.IDENTIFIER().getSymbol(), "property");
}
}
}
@Override
public void exitFunctionOrVar(FunctionOrVarContext ctx) {
if (ctx.methodArgs() != null) {
semantics.put(ctx.IDENTIFIER().getSymbol(), "method");
}
}
@Override
public void exitConstructorReference(ConstructorReferenceContext ctx) {
PossiblyQualifiedIdContext qualified = ctx.possiblyQualifiedId();
if (qualified != null) {
semantics.put(qualified.IDENTIFIER(qualified.IDENTIFIER().size() - 1).getSymbol(), "method");
}
}
@Override
public void exitPossiblyQualifiedId(PossiblyQualifiedIdContext ctx) {
if (ctx.IDENTIFIER() != null) {
for (int i = 0; i < ctx.IDENTIFIER().size() - 1; i++) {
semantics.put(ctx.IDENTIFIER(i).getSymbol(), "namespace");
}
semantics.put(ctx.IDENTIFIER(ctx.IDENTIFIER().size() - 1).getSymbol(), "type");
}
}
@Override
public void visitTerminal(TerminalNode node) {
processTerminalNode(node);
}
@Override
public void visitErrorNode(ErrorNode node) {
processTerminalNode(node);
}
});
parser.spelExpr();
List<SemanticTokenData> tokens = semantics.entrySet().stream()
.map(e -> new SemanticTokenData(e.getKey().getStartIndex() + initialOffset,
e.getKey().getStartIndex() + e.getKey().getText().length() + initialOffset, e.getValue(),
new String[0]))
.collect(Collectors.toList());
Collections.sort(tokens);
return tokens;
}
}

View File

@@ -16,6 +16,7 @@ import java.util.Set;
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.boot.app.BootLanguageServerParams;
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
@@ -41,7 +42,8 @@ public class SpringXMLLanguageServerComponents implements LanguageServerComponen
SimpleLanguageServer server,
SpringSymbolIndex springIndexer,
BootLanguageServerParams serverParams,
BootJavaConfig config) {
BootJavaConfig config,
SpelReconciler spelReconciler) {
this.projectFinder = serverParams.projectFinder;
this.docSymbolProvider = params -> springIndexer.getSymbols(params.getTextDocument().getUri());
@@ -49,12 +51,7 @@ public class SpringXMLLanguageServerComponents implements LanguageServerComponen
server.doOnInitialized(this::initialized);
server.onShutdown(this::shutdown);
this.reconcileEngine = new SpringXMLReconcileEngine(projectFinder, config);
config.addListener(ignore -> {
reconcileEngine.setSpelExpressionSyntaxValidationEnabled(config.isSpelExpressionValidationEnabled() && config.isSpringXMLSupportEnabled());
});
this.reconcileEngine = new SpringXMLReconcileEngine(projectFinder, config, spelReconciler);
}
@Override

View File

@@ -23,7 +23,7 @@ import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.dom.DOMParser;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.boot.java.handlers.SpelExpressionReconciler;
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
@@ -36,15 +36,15 @@ import org.springframework.ide.vscode.commons.util.text.IDocument;
public class SpringXMLReconcileEngine implements IReconcileEngine {
private final JavaProjectFinder projectFinder;
private final SpelExpressionReconciler spelExpressionReconciler;
private final SpelReconciler spelExpressionReconciler;
private final XMLElementReconciler[] reconcilers;
private final BootJavaConfig config;
public SpringXMLReconcileEngine(JavaProjectFinder projectFinder, BootJavaConfig config) {
public SpringXMLReconcileEngine(JavaProjectFinder projectFinder, BootJavaConfig config, SpelReconciler spelReconciler) {
this.projectFinder = projectFinder;
this.config = config;
this.spelExpressionReconciler = new SpelExpressionReconciler();
this.spelExpressionReconciler = spelReconciler;
this.reconcilers = new XMLElementReconciler[] {

View File

@@ -13,13 +13,21 @@ package org.springframework.ide.vscode.boot.java.data.jpa.queries;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
public class HqlSemanticTokensTest {
private HqlSemanticTokens provider = new HqlSemanticTokens();
private HqlSemanticTokens provider;
@BeforeEach
void setup() {
provider = new HqlSemanticTokens(Optional.empty());
}
@Test
void simpleQuery_1() {
@@ -108,9 +116,39 @@ public class HqlSemanticTokensTest {
assertThat(tokens.get(14)).isEqualTo(new SemanticTokenData(69, 71, "method", new String[0])); // id
assertThat(tokens.get(15)).isEqualTo(new SemanticTokenData(72, 73, "operator", new String[0])); // =
assertThat(tokens.get(16)).isEqualTo(new SemanticTokenData(73, 74, "operator", new String[0])); // :
assertThat(tokens.get(17)).isEqualTo(new SemanticTokenData(74, 79, "regexp", new String[0])); // #{id}
assertThat(tokens.get(17)).isEqualTo(new SemanticTokenData(74, 76, "operator", new String[0])); // #{
assertThat(tokens.get(18)).isEqualTo(new SemanticTokenData(76, 78, "string", new String[0])); // id
assertThat(tokens.get(19)).isEqualTo(new SemanticTokenData(78, 79, "operator", new String[0])); // }
assertThat(tokens.size()).isEqualTo(18);
assertThat(tokens.size()).isEqualTo(20);
}
@Test
void query_with_SPEL_Tokens() {
provider = new HqlSemanticTokens(Optional.of(new SpelSemanticTokens()));
List<SemanticTokenData> tokens = provider.computeTokens("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:#{id}", 0);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 6, "keyword", new String[0])); // SELECT
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(7, 12, "variable", new String[0])); // owner
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(13, 17, "keyword", new String[0])); // FROM
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(18, 23, "class", new String[0])); // Owner
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(24, 29, "variable", new String[0])); // owner
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(30, 34, "keyword", new String[0])); // left
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(35, 39, "keyword", new String[0])); // join
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(40, 45, "keyword", new String[0])); // fetch
assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(46, 51, "variable", new String[0])); // owner
assertThat(tokens.get(9)).isEqualTo(new SemanticTokenData(51, 52, "operator", new String[0])); // .
assertThat(tokens.get(10)).isEqualTo(new SemanticTokenData(52, 56, "method", new String[0])); // pets
assertThat(tokens.get(11)).isEqualTo(new SemanticTokenData(57, 62, "keyword", new String[0])); // WHERE
assertThat(tokens.get(12)).isEqualTo(new SemanticTokenData(63, 68, "variable", new String[0])); // owner
assertThat(tokens.get(13)).isEqualTo(new SemanticTokenData(68, 69, "operator", new String[0])); // .
assertThat(tokens.get(14)).isEqualTo(new SemanticTokenData(69, 71, "method", new String[0])); // id
assertThat(tokens.get(15)).isEqualTo(new SemanticTokenData(72, 73, "operator", new String[0])); // =
assertThat(tokens.get(16)).isEqualTo(new SemanticTokenData(73, 74, "operator", new String[0])); // :
assertThat(tokens.get(17)).isEqualTo(new SemanticTokenData(74, 76, "operator", new String[0])); // #{
assertThat(tokens.get(18)).isEqualTo(new SemanticTokenData(76, 78, "variable", new String[0])); // id
assertThat(tokens.get(19)).isEqualTo(new SemanticTokenData(78, 79, "operator", new String[0])); // }
assertThat(tokens.size()).isEqualTo(20);
}
}

View File

@@ -17,6 +17,7 @@ import java.util.List;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -354,4 +355,78 @@ public class JdtDataQuerySemanticTokensProviderTest {
}
@Test
@Disabled("Needs proper SPEL support in SQL grammar")
void nativeQueryWithSpel() throws Exception {
String source = """
package my.package
import org.springframework.data.jpa.repository.Query;
public interface OwnerRepository {
@Query(value = "SELECT * FROM USERS u WHERE u.status = ?#{status}", nativeQuery = true)
void findByLastName();
}
""";
String uri = Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/OwnerRepository.java").toUri().toASCIIString();
CompilationUnit cu = CompilationUnitCache.parse2(source.toCharArray(), uri, "OwnerRepository.java", jp);
assertThat(cu).isNotNull();
List<SemanticTokenData> tokens = computeTokens(cu);
SemanticTokenData token = tokens.get(0);
assertThat(source.substring(token.start(), token.end())).isEqualTo("SELECT");
assertThat(token).isEqualTo(new SemanticTokenData(128, 134, "keyword", new String[0]));
token = tokens.get(1);
assertThat(source.substring(token.start(), token.end())).isEqualTo("*");
assertThat(token).isEqualTo(new SemanticTokenData(135, 136, "operator", new String[0]));
token = tokens.get(2);
assertThat(source.substring(token.start(), token.end())).isEqualTo("FROM");
assertThat(token).isEqualTo(new SemanticTokenData(137, 141, "keyword", new String[0]));
token = tokens.get(3);
assertThat(source.substring(token.start(), token.end())).isEqualTo("USERS");
assertThat(token).isEqualTo(new SemanticTokenData(142, 147, "variable", new String[0]));
token = tokens.get(4);
assertThat(source.substring(token.start(), token.end())).isEqualTo("u");
assertThat(token).isEqualTo(new SemanticTokenData(148, 149, "variable", new String[0]));
token = tokens.get(5);
assertThat(source.substring(token.start(), token.end())).isEqualTo("WHERE");
assertThat(token).isEqualTo(new SemanticTokenData(150, 155, "keyword", new String[0]));
token = tokens.get(6);
assertThat(source.substring(token.start(), token.end())).isEqualTo("u");
assertThat(token).isEqualTo(new SemanticTokenData(156, 157, "variable", new String[0]));
token = tokens.get(7);
assertThat(source.substring(token.start(), token.end())).isEqualTo(".status");
assertThat(token).isEqualTo(new SemanticTokenData(157, 164, "variable", new String[0]));
token = tokens.get(8);
assertThat(source.substring(token.start(), token.end())).isEqualTo("=");
assertThat(token).isEqualTo(new SemanticTokenData(165, 166, "operator", new String[0]));
token = tokens.get(9);
assertThat(source.substring(token.start(), token.end())).isEqualTo("?");
assertThat(token).isEqualTo(new SemanticTokenData(167, 168, "operator", new String[0]));
token = tokens.get(9);
assertThat(source.substring(token.start(), token.end())).isEqualTo("#{");
assertThat(token).isEqualTo(new SemanticTokenData(168, 170, "operator", new String[0]));
token = tokens.get(10);
assertThat(source.substring(token.start(), token.end())).isEqualTo("status");
assertThat(token).isEqualTo(new SemanticTokenData(170, 176, "variable", new String[0]));
token = tokens.get(11);
assertThat(source.substring(token.start(), token.end())).isEqualTo("}");
assertThat(token).isEqualTo(new SemanticTokenData(176, 177, "operator", new String[0]));
}
}

View File

@@ -13,13 +13,21 @@ package org.springframework.ide.vscode.boot.java.data.jpa.queries;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
public class JpqlSemanticTokensTest {
private JpqlSemanticTokens provider = new JpqlSemanticTokens();
private JpqlSemanticTokens provider;
@BeforeEach
void setup() {
provider = new JpqlSemanticTokens(Optional.empty());
}
@Test
void simpleQuery_1() {
@@ -109,8 +117,39 @@ public class JpqlSemanticTokensTest {
assertThat(tokens.get(14)).isEqualTo(new SemanticTokenData(69, 71, "method", new String[0])); // id
assertThat(tokens.get(15)).isEqualTo(new SemanticTokenData(72, 73, "operator", new String[0])); // =
assertThat(tokens.get(16)).isEqualTo(new SemanticTokenData(73, 74, "operator", new String[0])); // :
assertThat(tokens.get(17)).isEqualTo(new SemanticTokenData(74, 79, "regexp", new String[0])); // #{id}
assertThat(tokens.get(17)).isEqualTo(new SemanticTokenData(74, 76, "operator", new String[0])); // #{
assertThat(tokens.get(18)).isEqualTo(new SemanticTokenData(76, 78, "string", new String[0])); // id
assertThat(tokens.get(19)).isEqualTo(new SemanticTokenData(78, 79, "operator", new String[0])); // }
assertThat(tokens.size()).isEqualTo(18);
assertThat(tokens.size()).isEqualTo(20);
}
@Test
void query_with_SPEL_Tokens() {
provider = new JpqlSemanticTokens(Optional.of(new SpelSemanticTokens()));
List<SemanticTokenData> tokens = provider.computeTokens("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:#{id}", 0);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 6, "keyword", new String[0])); // SELECT
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(7, 12, "variable", new String[0])); // owner
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(13, 17, "keyword", new String[0])); // FROM
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(18, 23, "class", new String[0])); // Owner
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(24, 29, "variable", new String[0])); // owner
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(30, 34, "keyword", new String[0])); // left
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(35, 39, "keyword", new String[0])); // join
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(40, 45, "keyword", new String[0])); // fetch
assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(46, 51, "variable", new String[0])); // owner
assertThat(tokens.get(9)).isEqualTo(new SemanticTokenData(51, 52, "operator", new String[0])); // .
assertThat(tokens.get(10)).isEqualTo(new SemanticTokenData(52, 56, "method", new String[0])); // pets
assertThat(tokens.get(11)).isEqualTo(new SemanticTokenData(57, 62, "keyword", new String[0])); // WHERE
assertThat(tokens.get(12)).isEqualTo(new SemanticTokenData(63, 68, "variable", new String[0])); // owner
assertThat(tokens.get(13)).isEqualTo(new SemanticTokenData(68, 69, "operator", new String[0])); // .
assertThat(tokens.get(14)).isEqualTo(new SemanticTokenData(69, 71, "method", new String[0])); // id
assertThat(tokens.get(15)).isEqualTo(new SemanticTokenData(72, 73, "operator", new String[0])); // =
assertThat(tokens.get(16)).isEqualTo(new SemanticTokenData(73, 74, "operator", new String[0])); // :
assertThat(tokens.get(17)).isEqualTo(new SemanticTokenData(74, 76, "operator", new String[0])); // #{
assertThat(tokens.get(18)).isEqualTo(new SemanticTokenData(76, 78, "variable", new String[0])); // id
assertThat(tokens.get(19)).isEqualTo(new SemanticTokenData(78, 79, "operator", new String[0])); // }
assertThat(tokens.size()).isEqualTo(20);
}
}

View File

@@ -0,0 +1,158 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.spel;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.file.Paths;
import java.util.List;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
import org.springframework.ide.vscode.boot.bootiful.HoverTestConf;
import org.springframework.ide.vscode.boot.java.reconcilers.CompositeASTVisitor;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.commons.util.Collector;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@BootLanguageServerTest
@Import(HoverTestConf.class)
public class JdtSpelSemanticTokensProviderTest {
@Autowired JdtSpelSemanticTokensProvider provider;
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
private MavenJavaProject jp;
@BeforeEach
public void setup() throws Exception {
jp = projects.mavenProject("spring-modulith-example-full");
}
private List<SemanticTokenData> computeTokens(CompilationUnit cu) {
Collector<SemanticTokenData> collector = new Collector<>();
CompositeASTVisitor visitor = new CompositeASTVisitor();
visitor.add(provider.getTokensComputer(jp, null, cu, collector));
cu.accept(visitor);
return collector.get();
}
@Test
void normalAnnotation() throws Exception {
String source = """
package my.package
import org.springframework.beans.factory.annotation.Value;
public class Owner {
@Value(value="#{new String('hello world').toUpperCase()}")
String s;
}
""";
String uri = Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/OwnerRepository.java").toUri().toASCIIString();
CompilationUnit cu = CompilationUnitCache.parse2(source.toCharArray(), uri, "OwnerRepository.java", jp);
assertThat(cu).isNotNull();
List<SemanticTokenData> tokens = computeTokens(cu);
assertThat(tokens.size()).isEqualTo(9);
SemanticTokenData token = tokens.get(0);
assertThat(token).isEqualTo(new SemanticTokenData(119, 122, "keyword", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo("new");
token = tokens.get(1);
assertThat(token).isEqualTo(new SemanticTokenData(123, 129, "method", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo("String");
token = tokens.get(2);
assertThat(token).isEqualTo(new SemanticTokenData(129, 130, "operator", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo("(");
token = tokens.get(3);
assertThat(token).isEqualTo(new SemanticTokenData(130, 143, "string", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo("'hello world'");
token = tokens.get(4);
assertThat(token).isEqualTo(new SemanticTokenData(143, 144, "operator", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo(")");
token = tokens.get(5);
assertThat(token).isEqualTo(new SemanticTokenData(144, 145, "operator", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo(".");
token = tokens.get(6);
assertThat(token).isEqualTo(new SemanticTokenData(145, 156, "method", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo("toUpperCase");
token = tokens.get(7);
assertThat(token).isEqualTo(new SemanticTokenData(156, 157, "operator", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo("(");
token = tokens.get(8);
assertThat(token).isEqualTo(new SemanticTokenData(157, 158, "operator", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo(")");
}
@Test
void singleMemberAnnotation() throws Exception {
String source = """
package my.package
import org.springframework.beans.factory.annotation.Value;
public class Owner {
@Value("#{'invalid alphabetic string #$1' matches '[a-zA-Z\\s]+' }")
String s;
}
""";
String uri = Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/OwnerRepository.java").toUri().toASCIIString();
CompilationUnit cu = CompilationUnitCache.parse2(source.toCharArray(), uri, "OwnerRepository.java", jp);
assertThat(cu).isNotNull();
List<SemanticTokenData> tokens = computeTokens(cu);
assertThat(tokens.size()).isEqualTo(3);
SemanticTokenData token = tokens.get(0);
assertThat(token).isEqualTo(new SemanticTokenData(113, 144, "string", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo("'invalid alphabetic string #$1'");
token = tokens.get(1);
assertThat(token).isEqualTo(new SemanticTokenData(145, 152, "keyword", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo("matches");
token = tokens.get(2);
assertThat(token).isEqualTo(new SemanticTokenData(153, 166, "string", new String[0]));
assertThat(source.substring(token.start(), token.end())).isEqualTo("'[a-zA-Z\\s]+'");
}
}

View File

@@ -0,0 +1,128 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.spel;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
public class SpelSemanticTokensTest {
private SpelSemanticTokens provider = new SpelSemanticTokens();
@Test
void simpleCompare() {
List<SemanticTokenData> tokens = provider.computeTokens("1 >= 1", 0);
assertThat(tokens.size()).isEqualTo(3);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0]));
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 4, "operator", new String[0]));
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(5, 6, "number", new String[0]));
}
@Test
void simpleCompareWithOp() {
List<SemanticTokenData> tokens = provider.computeTokens("1 ge 1", 0);
assertThat(tokens.size()).isEqualTo(3);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0]));
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 4, "keyword", new String[0]));
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(5, 6, "number", new String[0]));
}
@Test
void logicalOperators() {
List<SemanticTokenData> tokens = provider.computeTokens("4 > 3 or 15 < 10", 0);
assertThat(tokens.size()).isEqualTo(7);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "number", new String[0]));
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(2, 3, "operator", new String[0]));
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(4, 5, "number", new String[0]));
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(6, 8, "keyword", new String[0]));
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(9, 11, "number", new String[0]));
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(12, 13, "operator", new String[0]));
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(14, 16, "number", new String[0]));
}
@Test
void conditionalOperators() {
List<SemanticTokenData> tokens = provider.computeTokens("someBean.someProperty != null ? someBean.someProperty : 'default'", 0);
assertThat(tokens.size()).isEqualTo(11);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 8, "variable", new String[0]));
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(8, 9, "operator", new String[0]));
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(9, 21, "property", new String[0]));
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(22, 24, "operator", new String[0]));
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(25, 29, "keyword", new String[0]));
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(30, 31, "operator", new String[0]));
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(32, 40, "variable", new String[0]));
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(40, 41, "operator", new String[0]));
assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(41, 53, "property", new String[0]));
assertThat(tokens.get(9)).isEqualTo(new SemanticTokenData(54, 55, "operator", new String[0]));
assertThat(tokens.get(10)).isEqualTo(new SemanticTokenData(56, 65, "string", new String[0]));
}
@Test
void regex() {
List<SemanticTokenData> tokens = provider.computeTokens("'invalid alphabetic string #$1' matches '[a-zA-Z\\s]+' ", 0);
assertThat(tokens.size()).isEqualTo(3);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 31, "string", new String[0]));
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(32, 39, "keyword", new String[0]));
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(40, 53, "string", new String[0]));
}
@Test
void accessMapObj() {
List<SemanticTokenData> tokens = provider.computeTokens("carPark.carsByDriver['Driver1']", 0);
assertThat(tokens.size()).isEqualTo(6);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 7, "variable", new String[0]));
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(7, 8, "operator", new String[0]));
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(8, 20, "property", new String[0]));
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(20, 21, "operator", new String[0]));
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(21, 30, "string", new String[0]));
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(30, 31, "operator", new String[0]));
}
@Test
void accessListItems() {
List<SemanticTokenData> tokens = provider.computeTokens("carPark.cars[0]", 0);
assertThat(tokens.size()).isEqualTo(6);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 7, "variable", new String[0]));
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(7, 8, "operator", new String[0]));
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(8, 12, "property", new String[0]));
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(12, 13, "operator", new String[0]));
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(13, 14, "number", new String[0]));
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(14, 15, "operator", new String[0]));
}
@Test
void beanReference() {
List<SemanticTokenData> tokens = provider.computeTokens("@vetRepo", 0);
assertThat(tokens.size()).isEqualTo(2);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 1, "operator", new String[0]));
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(1, 8, "type", new String[0]));
}
@Test
void contructorAndMethod() {
List<SemanticTokenData> tokens = provider.computeTokens("new String('hello world').toUpperCase()", 0);
assertThat(tokens.size()).isEqualTo(9);
assertThat(tokens.get(0)).isEqualTo(new SemanticTokenData(0, 3, "keyword", new String[0]));
assertThat(tokens.get(1)).isEqualTo(new SemanticTokenData(4, 10, "method", new String[0]));
assertThat(tokens.get(2)).isEqualTo(new SemanticTokenData(10, 11, "operator", new String[0]));
assertThat(tokens.get(3)).isEqualTo(new SemanticTokenData(11, 24, "string", new String[0]));
assertThat(tokens.get(4)).isEqualTo(new SemanticTokenData(24, 25, "operator", new String[0]));
assertThat(tokens.get(5)).isEqualTo(new SemanticTokenData(25, 26, "operator", new String[0]));
assertThat(tokens.get(6)).isEqualTo(new SemanticTokenData(26, 37, "method", new String[0]));
assertThat(tokens.get(7)).isEqualTo(new SemanticTokenData(37, 38, "operator", new String[0]));
assertThat(tokens.get(8)).isEqualTo(new SemanticTokenData(38, 39, "operator", new String[0]));
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2023 Pivotal, Inc.
* Copyright (c) 2020, 2024 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
@@ -15,6 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.File;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -45,10 +46,10 @@ import org.springframework.ide.vscode.boot.index.cache.IndexCacheVoid;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine;
import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.reconcilers.AnnotationNodeReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler;
import org.springframework.ide.vscode.boot.java.spel.JdtSpelReconciler;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
import org.springframework.ide.vscode.boot.java.utils.test.MockProjectObserver;
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry;
@@ -79,11 +80,11 @@ import com.google.gson.JsonElement;
public class ValueSpelExpressionValidationTest {
@Autowired private BootLanguageServerHarness harness;
@Autowired private IJavaProject testProject;
@Autowired private JavaProjectFinder projectFinder;
@Autowired private CompilationUnitCache compilationUnitCache;
@Autowired private SimpleLanguageServer server;
@Autowired private BootJavaConfig config;
@Autowired private JdtSpelReconciler jdtSpelReconciler;
private File directory;
private String docUri;
@@ -149,8 +150,20 @@ public class ValueSpelExpressionValidationTest {
@BeforeEach
public void setup() throws Exception {
harness.intialize(null);
String changedSettings = "{\"boot-java\": {\"validation\": {\"java\": { \"reconcilers\": true}}}}";
String changedSettings = """
{
"boot-java": {
"validation": {
"java": {
"reconcilers": true
},
"spel": {
"on": "ON"
}
}
}
}
""";
JsonElement settingsAsJson = new Gson().fromJson(changedSettings, JsonElement.class);
harness.changeConfiguration(new Settings(settingsAsJson));
@@ -160,7 +173,7 @@ public class ValueSpelExpressionValidationTest {
problemCollector = new TestProblemCollector();
reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] {
new JdtReconciler(compilationUnitCache, config, new JdtAstReconciler[] {
new AnnotationNodeReconciler(config)
jdtSpelReconciler
}, new MockProjectObserver())
});
}
@@ -328,7 +341,7 @@ public class ValueSpelExpressionValidationTest {
}
private TextDocument prepareDocument(String selectedAnnotation, String annotationStatementBeforeTest) throws Exception {
String content = IOUtils.toString(new URI(docUri));
String content = IOUtils.toString(new URI(docUri), StandardCharsets.UTF_8);
TextDocumentItem docItem = new TextDocumentItem(docUri, LanguageId.JAVA.toString(), 0, content);
DidOpenTextDocumentParams openParams = new DidOpenTextDocumentParams(docItem);

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2023 Pivotal, Inc.
* Copyright (c) 2020, 2024 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
@@ -15,6 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.File;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -35,6 +36,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.boot.app.BootLanguageServerBootApp;
import org.springframework.ide.vscode.boot.bootiful.XmlBeansTestConf;
import org.springframework.ide.vscode.boot.java.spel.SpelReconciler;
import org.springframework.ide.vscode.boot.xml.SpringXMLReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
@@ -67,6 +69,7 @@ public class XMLSpelExpressionValidationTest {
@Autowired private SimpleLanguageServer server;
@Autowired private JavaProjectFinder projectFinder;
@Autowired private BootJavaConfig config;
@Autowired private SpelReconciler spelReconciler;
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
private MavenJavaProject project;
@@ -97,7 +100,7 @@ public class XMLSpelExpressionValidationTest {
docUri = directory.toPath().resolve("src/main/webapp/WEB-INF/spring/root-context.xml").toUri().toASCIIString();
problemCollector = new TestProblemCollector();
reconcileEngine = new SpringXMLReconcileEngine(projectFinder, config);
reconcileEngine = new SpringXMLReconcileEngine(projectFinder, config, spelReconciler);
}
@AfterEach
@@ -142,7 +145,7 @@ public class XMLSpelExpressionValidationTest {
}
private TextDocument prepareDocument(String selectedAnnotation, String annotationStatementBeforeTest) throws Exception {
String content = IOUtils.toString(new URI(docUri));
String content = IOUtils.toString(new URI(docUri), StandardCharsets.UTF_8);
TextDocumentItem docItem = new TextDocumentItem(docUri, LanguageId.XML.toString(), 0, content);
DidOpenTextDocumentParams openParams = new DidOpenTextDocumentParams(docItem);

View File

@@ -1195,4 +1195,4 @@
"extensionDependencies": [
"redhat.java"
]
}
}