Merge branch '6.0.x'

This commit is contained in:
Juergen Hoeller 2023-08-23 19:02:44 +02:00
commit ecc0a6d2db
4 changed files with 70 additions and 53 deletions

View File

@ -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;

View File

@ -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,20 +254,20 @@ 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,18 +334,21 @@ 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();
@ -354,7 +356,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
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);
} }
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(); 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;

View File

@ -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.
@ -65,7 +65,7 @@ class Token {
public boolean isNumericRelationalOperator() { public boolean isNumericRelationalOperator() {
return (this.kind == TokenKind.GT || this.kind == TokenKind.GE || this.kind == TokenKind.LT || return (this.kind == TokenKind.GT || this.kind == TokenKind.GE || this.kind == TokenKind.LT ||
this.kind == TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE); this.kind == TokenKind.LE || this.kind == TokenKind.EQ || this.kind == TokenKind.NE);
} }
public String stringValue() { public String stringValue() {
@ -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();
} }
} }

View File

@ -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;
@ -76,8 +78,8 @@ class SpelParserTests {
private static void assertNullOrEmptyExpressionIsRejected(ThrowingCallable throwingCallable) { private static void assertNullOrEmptyExpressionIsRejected(ThrowingCallable throwingCallable) {
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(throwingCallable) .isThrownBy(throwingCallable)
.withMessage("'expressionString' must not be null or blank"); .withMessage("'expressionString' must not be null or blank");
} }
@Test @Test
@ -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
@ -377,7 +385,7 @@ class SpelParserTests {
private void checkNumberError(String expression, SpelMessage expectedMessage) { private void checkNumberError(String expression, SpelMessage expectedMessage) {
assertParseExceptionThrownBy(() -> parser.parseRaw(expression)) assertParseExceptionThrownBy(() -> parser.parseRaw(expression))
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(expectedMessage)); .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(expectedMessage));
} }
private static ThrowableAssertAlternative<SpelParseException> assertParseExceptionThrownBy(ThrowingCallable throwingCallable) { private static ThrowableAssertAlternative<SpelParseException> assertParseExceptionThrownBy(ThrowingCallable throwingCallable) {
@ -386,15 +394,18 @@ class SpelParserTests {
private static void assertParseException(ThrowingCallable throwingCallable, SpelMessage expectedMessage, int expectedPosition) { private static void assertParseException(ThrowingCallable throwingCallable, SpelMessage expectedMessage, int expectedPosition) {
assertParseExceptionThrownBy(throwingCallable) assertParseExceptionThrownBy(throwingCallable)
.satisfies(parseExceptionRequirements(expectedMessage, expectedPosition)); .satisfies(parseExceptionRequirements(expectedMessage, expectedPosition));
} }
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);
assertThat(ex.getMessage()).contains(ex.getExpressionString()); if (ex.getExpressionString() != null) {
assertThat(ex.getMessage()).contains(ex.getExpressionString());
}
}; };
} }