|
|
|
@ -77,7 +77,6 @@ import org.springframework.expression.spel.ast.Ternary;
@@ -77,7 +77,6 @@ import org.springframework.expression.spel.ast.Ternary;
|
|
|
|
|
import org.springframework.expression.spel.ast.TypeReference; |
|
|
|
|
import org.springframework.expression.spel.ast.VariableReference; |
|
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
|
import org.springframework.util.Assert; |
|
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -137,12 +136,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -137,12 +136,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
this.tokenStreamPointer = 0; |
|
|
|
|
this.constructedNodes.clear(); |
|
|
|
|
SpelNodeImpl ast = eatExpression(); |
|
|
|
|
Assert.state(ast != null, "No node"); |
|
|
|
|
if (ast == null) { |
|
|
|
|
throw new SpelParseException(this.expressionString, 0, SpelMessage.OOD); |
|
|
|
|
} |
|
|
|
|
Token t = peekToken(); |
|
|
|
|
if (t != null) { |
|
|
|
|
throw new SpelParseException(t.startPos, SpelMessage.MORE_INPUT, toString(nextToken())); |
|
|
|
|
throw new SpelParseException(this.expressionString, t.startPos, SpelMessage.MORE_INPUT, toString(nextToken())); |
|
|
|
|
} |
|
|
|
|
Assert.isTrue(this.constructedNodes.isEmpty(), "At least one node expected"); |
|
|
|
|
return new SpelExpression(expressionString, ast, this.configuration); |
|
|
|
|
} |
|
|
|
|
catch (InternalParseException ex) { |
|
|
|
@ -254,20 +254,20 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -254,20 +254,20 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
if (tk == TokenKind.EQ) { |
|
|
|
|
return new OpEQ(t.startPos, t.endPos, expr, rhExpr); |
|
|
|
|
} |
|
|
|
|
Assert.isTrue(tk == TokenKind.NE, "Not-equals token expected"); |
|
|
|
|
return new OpNE(t.startPos, t.endPos, expr, rhExpr); |
|
|
|
|
if (tk == TokenKind.NE) { |
|
|
|
|
return new OpNE(t.startPos, t.endPos, expr, rhExpr); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (tk == TokenKind.INSTANCEOF) { |
|
|
|
|
return new OperatorInstanceof(t.startPos, t.endPos, expr, rhExpr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (tk == TokenKind.MATCHES) { |
|
|
|
|
return new OperatorMatches(this.patternCache, t.startPos, t.endPos, expr, rhExpr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Assert.isTrue(tk == TokenKind.BETWEEN, "Between token expected"); |
|
|
|
|
return new OperatorBetween(t.startPos, t.endPos, expr, rhExpr); |
|
|
|
|
if (tk == TokenKind.BETWEEN) { |
|
|
|
|
return new OperatorBetween(t.startPos, t.endPos, expr, rhExpr); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return expr; |
|
|
|
|
} |
|
|
|
@ -304,8 +304,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -304,8 +304,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
else if (t.kind == TokenKind.DIV) { |
|
|
|
|
expr = new OpDivide(t.startPos, t.endPos, expr, rhExpr); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
Assert.isTrue(t.kind == TokenKind.MOD, "Mod token expected"); |
|
|
|
|
else if (t.kind == TokenKind.MOD) { |
|
|
|
|
expr = new OpModulus(t.startPos, t.endPos, expr, rhExpr); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -335,18 +334,21 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -335,18 +334,21 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
// unaryExpression: (PLUS^ | MINUS^ | BANG^ | INC^ | DEC^) unaryExpression | primaryExpression ;
|
|
|
|
|
@Nullable |
|
|
|
|
private SpelNodeImpl eatUnaryExpression() { |
|
|
|
|
if (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) { |
|
|
|
|
if (peekToken(TokenKind.NOT, TokenKind.PLUS, TokenKind.MINUS)) { |
|
|
|
|
Token t = takeToken(); |
|
|
|
|
SpelNodeImpl expr = eatUnaryExpression(); |
|
|
|
|
Assert.state(expr != null, "No node"); |
|
|
|
|
if (expr == null) { |
|
|
|
|
throw internalException(t.startPos, SpelMessage.OOD); |
|
|
|
|
} |
|
|
|
|
if (t.kind == TokenKind.NOT) { |
|
|
|
|
return new OperatorNot(t.startPos, t.endPos, expr); |
|
|
|
|
} |
|
|
|
|
if (t.kind == TokenKind.PLUS) { |
|
|
|
|
return new OpPlus(t.startPos, t.endPos, expr); |
|
|
|
|
} |
|
|
|
|
Assert.isTrue(t.kind == TokenKind.MINUS, "Minus token expected"); |
|
|
|
|
return new OpMinus(t.startPos, t.endPos, expr); |
|
|
|
|
if (t.kind == TokenKind.MINUS) { |
|
|
|
|
return new OpMinus(t.startPos, t.endPos, expr); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (peekToken(TokenKind.INC, TokenKind.DEC)) { |
|
|
|
|
Token t = takeToken(); |
|
|
|
@ -354,7 +356,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -354,7 +356,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
if (t.getKind() == TokenKind.INC) { |
|
|
|
|
return new OpInc(t.startPos, t.endPos, false, expr); |
|
|
|
|
} |
|
|
|
|
return new OpDec(t.startPos, t.endPos, false, expr); |
|
|
|
|
if (t.kind == TokenKind.DEC) { |
|
|
|
|
return new OpDec(t.startPos, t.endPos, false, expr); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return eatPrimaryExpression(); |
|
|
|
|
} |
|
|
|
@ -414,7 +418,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -414,7 +418,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
return pop(); |
|
|
|
|
} |
|
|
|
|
if (peekToken() == null) { |
|
|
|
|
// unexpectedly ran out of data
|
|
|
|
|
throw internalException(t.startPos, SpelMessage.OOD); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
@ -460,8 +463,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -460,8 +463,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
|
|
|
|
|
private void eatConstructorArgs(List<SpelNodeImpl> accumulatedArguments) { |
|
|
|
|
if (!peekToken(TokenKind.LPAREN)) { |
|
|
|
|
throw new InternalParseException(new SpelParseException(this.expressionString, |
|
|
|
|
positionOf(peekToken()), SpelMessage.MISSING_CONSTRUCTOR_ARGS)); |
|
|
|
|
throw internalException(positionOf(peekToken()), SpelMessage.MISSING_CONSTRUCTOR_ARGS); |
|
|
|
|
} |
|
|
|
|
consumeArguments(accumulatedArguments); |
|
|
|
|
eatToken(TokenKind.RPAREN); |
|
|
|
@ -472,7 +474,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -472,7 +474,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
*/ |
|
|
|
|
private void consumeArguments(List<SpelNodeImpl> accumulatedArguments) { |
|
|
|
|
Token t = peekToken(); |
|
|
|
|
Assert.state(t != null, "Expected token"); |
|
|
|
|
if (t == null) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
int pos = t.startPos; |
|
|
|
|
Token next; |
|
|
|
|
do { |
|
|
|
@ -575,8 +579,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -575,8 +579,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
private boolean maybeEatTypeReference() { |
|
|
|
|
if (peekToken(TokenKind.IDENTIFIER)) { |
|
|
|
|
Token typeName = peekToken(); |
|
|
|
|
Assert.state(typeName != null, "Expected token"); |
|
|
|
|
if (!"T".equals(typeName.stringValue())) { |
|
|
|
|
if (typeName == null || !"T".equals(typeName.stringValue())) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
// It looks like a type reference but is T being used as a map key?
|
|
|
|
@ -605,8 +608,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -605,8 +608,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
private boolean maybeEatNullReference() { |
|
|
|
|
if (peekToken(TokenKind.IDENTIFIER)) { |
|
|
|
|
Token nullToken = peekToken(); |
|
|
|
|
Assert.state(nullToken != null, "Expected token"); |
|
|
|
|
if (!"null".equalsIgnoreCase(nullToken.stringValue())) { |
|
|
|
|
if (nullToken == null || !"null".equalsIgnoreCase(nullToken.stringValue())) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
nextToken(); |
|
|
|
@ -619,12 +621,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -619,12 +621,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
//projection: PROJECT^ expression RCURLY!;
|
|
|
|
|
private boolean maybeEatProjection(boolean nullSafeNavigation) { |
|
|
|
|
Token t = peekToken(); |
|
|
|
|
if (!peekToken(TokenKind.PROJECT, true)) { |
|
|
|
|
if (t == null || !peekToken(TokenKind.PROJECT, true)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
Assert.state(t != null, "No token"); |
|
|
|
|
SpelNodeImpl expr = eatExpression(); |
|
|
|
|
Assert.state(expr != null, "No node"); |
|
|
|
|
if (expr == null) { |
|
|
|
|
throw internalException(t.startPos, SpelMessage.OOD); |
|
|
|
|
} |
|
|
|
|
eatToken(TokenKind.RSQUARE); |
|
|
|
|
this.constructedNodes.push(new Projection(nullSafeNavigation, t.startPos, t.endPos, expr)); |
|
|
|
|
return true; |
|
|
|
@ -634,15 +637,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -634,15 +637,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
// map = LCURLY (key ':' value (COMMA key ':' value)*) RCURLY
|
|
|
|
|
private boolean maybeEatInlineListOrMap() { |
|
|
|
|
Token t = peekToken(); |
|
|
|
|
if (!peekToken(TokenKind.LCURLY, true)) { |
|
|
|
|
if (t == null || !peekToken(TokenKind.LCURLY, true)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
Assert.state(t != null, "No token"); |
|
|
|
|
SpelNodeImpl expr = null; |
|
|
|
|
Token closingCurly = peekToken(); |
|
|
|
|
if (peekToken(TokenKind.RCURLY, true)) { |
|
|
|
|
if (closingCurly != null && peekToken(TokenKind.RCURLY, true)) { |
|
|
|
|
// empty list '{}'
|
|
|
|
|
Assert.state(closingCurly != null, "No token"); |
|
|
|
|
expr = new InlineList(t.startPos, closingCurly.endPos); |
|
|
|
|
} |
|
|
|
|
else if (peekToken(TokenKind.COLON, true)) { |
|
|
|
@ -695,12 +696,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -695,12 +696,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
|
|
|
|
|
private boolean maybeEatIndexer() { |
|
|
|
|
Token t = peekToken(); |
|
|
|
|
if (!peekToken(TokenKind.LSQUARE, true)) { |
|
|
|
|
if (t == null || !peekToken(TokenKind.LSQUARE, true)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
Assert.state(t != null, "No token"); |
|
|
|
|
SpelNodeImpl expr = eatExpression(); |
|
|
|
|
Assert.state(expr != null, "No node"); |
|
|
|
|
if (expr == null) { |
|
|
|
|
throw internalException(t.startPos, SpelMessage.MISSING_SELECTION_EXPRESSION); |
|
|
|
|
} |
|
|
|
|
eatToken(TokenKind.RSQUARE); |
|
|
|
|
this.constructedNodes.push(new Indexer(t.startPos, t.endPos, expr)); |
|
|
|
|
return true; |
|
|
|
@ -708,10 +710,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -708,10 +710,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
|
|
|
|
|
private boolean maybeEatSelection(boolean nullSafeNavigation) { |
|
|
|
|
Token t = peekToken(); |
|
|
|
|
if (!peekSelectToken()) { |
|
|
|
|
if (t == null || !peekSelectToken()) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
Assert.state(t != null, "No token"); |
|
|
|
|
nextToken(); |
|
|
|
|
SpelNodeImpl expr = eatExpression(); |
|
|
|
|
if (expr == null) { |
|
|
|
@ -889,9 +890,14 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -889,9 +890,14 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
|
|
|
|
//parenExpr : LPAREN! expression RPAREN!;
|
|
|
|
|
private boolean maybeEatParenExpression() { |
|
|
|
|
if (peekToken(TokenKind.LPAREN)) { |
|
|
|
|
nextToken(); |
|
|
|
|
Token t = nextToken(); |
|
|
|
|
if (t == null) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
SpelNodeImpl expr = eatExpression(); |
|
|
|
|
Assert.state(expr != null, "No node"); |
|
|
|
|
if (expr == null) { |
|
|
|
|
throw internalException(t.startPos, SpelMessage.OOD); |
|
|
|
|
} |
|
|
|
|
eatToken(TokenKind.RPAREN); |
|
|
|
|
push(expr); |
|
|
|
|
return true; |
|
|
|
|