Browse Source

Merge branch '6.0.x'

pull/31113/head
Juergen Hoeller 2 years ago
parent
commit
ecc0a6d2db
  1. 2
      spring-core/src/main/java/org/springframework/util/ClassUtils.java
  2. 76
      spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java
  3. 14
      spring-expression/src/main/java/org/springframework/expression/spel/standard/Token.java
  4. 11
      spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java

2
spring-core/src/main/java/org/springframework/util/ClassUtils.java

@ -1479,7 +1479,7 @@ public abstract class ClassUtils {
Assert.notNull(methodName, "Method name must not be null"); Assert.notNull(methodName, "Method name must not be null");
try { try {
Method method = clazz.getMethod(methodName, args); Method method = clazz.getMethod(methodName, args);
return Modifier.isStatic(method.getModifiers()) ? method : null; return (Modifier.isStatic(method.getModifiers()) ? method : null);
} }
catch (NoSuchMethodException ex) { catch (NoSuchMethodException ex) {
return null; return null;

76
spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java

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

14
spring-expression/src/main/java/org/springframework/expression/spel/standard/Token.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -87,14 +87,14 @@ class Token {
@Override @Override
public String toString() { public String toString() {
StringBuilder s = new StringBuilder(); StringBuilder sb = new StringBuilder();
s.append('[').append(this.kind.toString()); sb.append('[').append(this.kind);
if (this.kind.hasPayload()) { if (this.kind.hasPayload()) {
s.append(':').append(this.data); sb.append(':').append(this.data);
} }
s.append(']'); sb.append(']');
s.append('(').append(this.startPos).append(',').append(this.endPos).append(')'); sb.append('(').append(this.startPos).append(',').append(this.endPos).append(')');
return s.toString(); return sb.toString();
} }
} }

11
spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java

@ -39,7 +39,9 @@ import static org.springframework.expression.spel.SpelMessage.NON_TERMINATING_DO
import static org.springframework.expression.spel.SpelMessage.NON_TERMINATING_QUOTED_STRING; import static org.springframework.expression.spel.SpelMessage.NON_TERMINATING_QUOTED_STRING;
import static org.springframework.expression.spel.SpelMessage.NOT_AN_INTEGER; import static org.springframework.expression.spel.SpelMessage.NOT_AN_INTEGER;
import static org.springframework.expression.spel.SpelMessage.NOT_A_LONG; import static org.springframework.expression.spel.SpelMessage.NOT_A_LONG;
import static org.springframework.expression.spel.SpelMessage.OOD;
import static org.springframework.expression.spel.SpelMessage.REAL_CANNOT_BE_LONG; import static org.springframework.expression.spel.SpelMessage.REAL_CANNOT_BE_LONG;
import static org.springframework.expression.spel.SpelMessage.RIGHT_OPERAND_PROBLEM;
import static org.springframework.expression.spel.SpelMessage.RUN_OUT_OF_ARGUMENTS; import static org.springframework.expression.spel.SpelMessage.RUN_OUT_OF_ARGUMENTS;
import static org.springframework.expression.spel.SpelMessage.UNEXPECTED_DATA_AFTER_DOT; import static org.springframework.expression.spel.SpelMessage.UNEXPECTED_DATA_AFTER_DOT;
import static org.springframework.expression.spel.SpelMessage.UNEXPECTED_ESCAPE_CHAR; import static org.springframework.expression.spel.SpelMessage.UNEXPECTED_ESCAPE_CHAR;
@ -152,7 +154,13 @@ class SpelParserTests {
assertParseException(() -> parser.parseRaw("new String(3"), RUN_OUT_OF_ARGUMENTS, 10); assertParseException(() -> parser.parseRaw("new String(3"), RUN_OUT_OF_ARGUMENTS, 10);
assertParseException(() -> parser.parseRaw("new String("), RUN_OUT_OF_ARGUMENTS, 10); assertParseException(() -> parser.parseRaw("new String("), RUN_OUT_OF_ARGUMENTS, 10);
assertParseException(() -> parser.parseRaw("\"abc"), NON_TERMINATING_DOUBLE_QUOTED_STRING, 0); assertParseException(() -> parser.parseRaw("\"abc"), NON_TERMINATING_DOUBLE_QUOTED_STRING, 0);
assertParseException(() -> parser.parseRaw("abc\""), NON_TERMINATING_DOUBLE_QUOTED_STRING, 3);
assertParseException(() -> parser.parseRaw("'abc"), NON_TERMINATING_QUOTED_STRING, 0); assertParseException(() -> parser.parseRaw("'abc"), NON_TERMINATING_QUOTED_STRING, 0);
assertParseException(() -> parser.parseRaw("abc'"), NON_TERMINATING_QUOTED_STRING, 3);
assertParseException(() -> parser.parseRaw("("), OOD, 0);
assertParseException(() -> parser.parseRaw(")"), OOD, 0);
assertParseException(() -> parser.parseRaw("+"), OOD, 0);
assertParseException(() -> parser.parseRaw("1+"), RIGHT_OPERAND_PROBLEM, 1);
} }
@Test @Test
@ -391,10 +399,13 @@ class SpelParserTests {
private static <E extends SpelParseException> Consumer<E> parseExceptionRequirements( private static <E extends SpelParseException> Consumer<E> parseExceptionRequirements(
SpelMessage expectedMessage, int expectedPosition) { SpelMessage expectedMessage, int expectedPosition) {
return ex -> { return ex -> {
assertThat(ex.getMessageCode()).isEqualTo(expectedMessage); assertThat(ex.getMessageCode()).isEqualTo(expectedMessage);
assertThat(ex.getPosition()).isEqualTo(expectedPosition); assertThat(ex.getPosition()).isEqualTo(expectedPosition);
if (ex.getExpressionString() != null) {
assertThat(ex.getMessage()).contains(ex.getExpressionString()); assertThat(ex.getMessage()).contains(ex.getExpressionString());
}
}; };
} }

Loading…
Cancel
Save