Polishing
This commit is contained in:
parent
67958656e4
commit
2b52582dff
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -19,9 +19,9 @@ package org.springframework.expression;
|
|||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* By default the mathematical operators {@link Operation} support simple types
|
||||
* like numbers. By providing an implementation of OperatorOverloader, a user
|
||||
* of the expression language can support these operations on other types.
|
||||
* By default, the mathematical operators defined in {@link Operation} support simple
|
||||
* types like numbers. By providing an implementation of {@code OperatorOverloader},
|
||||
* a user of the expression language can support these operations on other types.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
|
|
@ -29,21 +29,21 @@ import org.springframework.lang.Nullable;
|
|||
public interface OperatorOverloader {
|
||||
|
||||
/**
|
||||
* Return true if the operator overloader supports the specified operation
|
||||
* between the two operands and so should be invoked to handle it.
|
||||
* Return {@code true} if this operator overloader supports the specified
|
||||
* operation on the two operands and should be invoked to handle it.
|
||||
* @param operation the operation to be performed
|
||||
* @param leftOperand the left operand
|
||||
* @param rightOperand the right operand
|
||||
* @return true if the OperatorOverloader supports the specified operation
|
||||
* between the two operands
|
||||
* @return true if this {@code OperatorOverloader} supports the specified
|
||||
* operation between the two operands
|
||||
* @throws EvaluationException if there is a problem performing the operation
|
||||
*/
|
||||
boolean overridesOperation(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand)
|
||||
throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Execute the specified operation on two operands, returning a result.
|
||||
* See {@link Operation} for supported operations.
|
||||
* Perform the specified operation on the two operands, returning a result.
|
||||
* <p>See {@link Operation} for supported operations.
|
||||
* @param operation the operation to be performed
|
||||
* @param leftOperand the left operand
|
||||
* @param rightOperand the right operand
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -24,17 +24,19 @@ import org.springframework.expression.TypedValue;
|
|||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a template expression broken into pieces. Each piece will be an Expression
|
||||
* but pure text parts to the template will be represented as LiteralExpression objects.
|
||||
* An example of a template expression might be:
|
||||
* Represents a template expression broken into pieces.
|
||||
*
|
||||
* <p>Each piece will be an {@link Expression}, but pure text parts of the
|
||||
* template will be represented as {@link LiteralExpression} objects. An example
|
||||
* of a template expression might be:
|
||||
*
|
||||
* <pre class="code">
|
||||
* "Hello ${getName()}"
|
||||
* </pre>
|
||||
*
|
||||
* which will be represented as a CompositeStringExpression of two parts. The first part
|
||||
* being a LiteralExpression representing 'Hello ' and the second part being a real
|
||||
* expression that will call {@code getName()} when invoked.
|
||||
* which will be represented as a {@code CompositeStringExpression} of two parts:
|
||||
* the first part being a {@link LiteralExpression} representing 'Hello ' and the
|
||||
* second part being a real expression that will call {@code getName()} when invoked.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -24,9 +24,11 @@ import org.springframework.expression.TypedValue;
|
|||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A very simple hardcoded implementation of the Expression interface that represents a
|
||||
* string literal. It is used with CompositeStringExpression when representing a template
|
||||
* expression which is made up of pieces - some being real expressions to be handled by
|
||||
* A very simple, hard-coded implementation of the {@link Expression} interface
|
||||
* that represents a string literal.
|
||||
*
|
||||
* <p>It is used with {@link CompositeStringExpression} when representing a template
|
||||
* expression which is made up of pieces, some being real expressions to be handled by
|
||||
* an EL implementation like SpEL, and some being just textual elements.
|
||||
*
|
||||
* @author Andy Clement
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -80,11 +80,11 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
|
|||
* result, evaluating all returned expressions and concatenating the results produces
|
||||
* the complete evaluated string. Unwrapping is only done of the outermost delimiters
|
||||
* found, so the string 'hello ${foo${abc}}' would break into the pieces 'hello ' and
|
||||
* 'foo${abc}'. This means that expression languages that used ${..} as part of their
|
||||
* 'foo${abc}'. This means that expression languages that use ${..} as part of their
|
||||
* functionality are supported without any problem. The parsing is aware of the
|
||||
* structure of an embedded expression. It assumes that parentheses '(', square
|
||||
* brackets '[' and curly brackets '}' must be in pairs within the expression unless
|
||||
* they are within a string literal and a string literal starts and terminates with a
|
||||
* brackets '[', and curly brackets '}' must be in pairs within the expression unless
|
||||
* they are within a string literal and the string literal starts and terminates with a
|
||||
* single quote '.
|
||||
* @param expressionString the expression string
|
||||
* @return the parsed expressions
|
||||
|
|
@ -238,48 +238,33 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
|
|||
/**
|
||||
* This captures a type of bracket and the position in which it occurs in the
|
||||
* expression. The positional information is used if an error has to be reported
|
||||
* because the related end bracket cannot be found. Bracket is used to describe:
|
||||
* square brackets [] round brackets () and curly brackets {}
|
||||
* because the related end bracket cannot be found. Bracket is used to describe
|
||||
* square brackets [], round brackets (), and curly brackets {}.
|
||||
*/
|
||||
private static class Bracket {
|
||||
|
||||
char bracket;
|
||||
|
||||
int pos;
|
||||
|
||||
Bracket(char bracket, int pos) {
|
||||
this.bracket = bracket;
|
||||
this.pos = pos;
|
||||
}
|
||||
private record Bracket(char bracket, int pos) {
|
||||
|
||||
boolean compatibleWithCloseBracket(char closeBracket) {
|
||||
if (this.bracket == '{') {
|
||||
return closeBracket == '}';
|
||||
}
|
||||
else if (this.bracket == '[') {
|
||||
return closeBracket == ']';
|
||||
}
|
||||
return closeBracket == ')';
|
||||
return switch (this.bracket) {
|
||||
case '{' -> closeBracket == '}';
|
||||
case '[' -> closeBracket == ']';
|
||||
default -> closeBracket == ')';
|
||||
};
|
||||
}
|
||||
|
||||
static char theOpenBracketFor(char closeBracket) {
|
||||
if (closeBracket == '}') {
|
||||
return '{';
|
||||
}
|
||||
else if (closeBracket == ']') {
|
||||
return '[';
|
||||
}
|
||||
return '(';
|
||||
return switch (closeBracket) {
|
||||
case '}' -> '{';
|
||||
case ']' -> '[';
|
||||
default -> '(';
|
||||
};
|
||||
}
|
||||
|
||||
static char theCloseBracketFor(char openBracket) {
|
||||
if (openBracket == '{') {
|
||||
return '}';
|
||||
}
|
||||
else if (openBracket == '[') {
|
||||
return ']';
|
||||
}
|
||||
return ')';
|
||||
return switch (openBracket) {
|
||||
case '{' -> '}';
|
||||
case '[' -> ']';
|
||||
default -> ')';
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
|
@ -223,7 +222,7 @@ class ExpressionStateTests extends AbstractExpressionTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void typeLocator() throws EvaluationException {
|
||||
void typeLocator() {
|
||||
assertThat(state.getEvaluationContext().getTypeLocator()).isNotNull();
|
||||
assertThat(state.findType("java.lang.Integer")).isEqualTo(Integer.class);
|
||||
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||
|
|
@ -232,7 +231,7 @@ class ExpressionStateTests extends AbstractExpressionTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void typeConversion() throws EvaluationException {
|
||||
void typeConversion() {
|
||||
String s = (String) state.convertValue(34, TypeDescriptor.valueOf(String.class));
|
||||
assertThat(s).isEqualTo("34");
|
||||
|
||||
|
|
|
|||
|
|
@ -18,56 +18,50 @@ package org.springframework.expression.spel;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.OperatorOverloader;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test providing operator support
|
||||
* Tests for custom {@link OperatorOverloader} support.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class OperatorOverloaderTests extends AbstractExpressionTests {
|
||||
|
||||
@Test
|
||||
void testSimpleOperations() {
|
||||
// no built-in support for this:
|
||||
evaluateAndCheckError("'abc'-true",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
void simpleOperations() {
|
||||
// default behavior
|
||||
evaluate("'abc' + true", "abctrue", String.class);
|
||||
evaluate("'abc' + null", "abcnull", String.class);
|
||||
|
||||
StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
|
||||
eContext.setOperatorOverloader(new StringAndBooleanAddition());
|
||||
// no built-in support for <string> - <boolean>
|
||||
evaluateAndCheckError("'abc' - true", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
|
||||
SpelExpression expr = (SpelExpression)parser.parseExpression("'abc'+true");
|
||||
assertThat(expr.getValue(eContext)).isEqualTo("abctrue");
|
||||
super.context.setOperatorOverloader(new StringAndBooleanOperatorOverloader());
|
||||
|
||||
expr = (SpelExpression)parser.parseExpression("'abc'-true");
|
||||
assertThat(expr.getValue(eContext)).isEqualTo("abc");
|
||||
// unaffected
|
||||
evaluate("'abc' + true", "abctrue", String.class);
|
||||
evaluate("'abc' + null", "abcnull", String.class);
|
||||
|
||||
expr = (SpelExpression)parser.parseExpression("'abc'+null");
|
||||
assertThat(expr.getValue(eContext)).isEqualTo("abcnull");
|
||||
// <string> - <boolean> has been overloaded
|
||||
evaluate("'abc' - true", "abcTRUE", String.class);
|
||||
}
|
||||
|
||||
|
||||
static class StringAndBooleanAddition implements OperatorOverloader {
|
||||
private static class StringAndBooleanOperatorOverloader implements OperatorOverloader {
|
||||
|
||||
@Override
|
||||
public Object operate(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException {
|
||||
if (operation==Operation.ADD) {
|
||||
return leftOperand + ((Boolean) rightOperand).toString();
|
||||
}
|
||||
else {
|
||||
return leftOperand;
|
||||
}
|
||||
public boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand) {
|
||||
return (leftOperand instanceof String && rightOperand instanceof Boolean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException {
|
||||
return leftOperand instanceof String && rightOperand instanceof Boolean;
|
||||
|
||||
public Object operate(Operation operation, Object leftOperand, Object rightOperand) {
|
||||
if (operation == Operation.SUBTRACT) {
|
||||
return leftOperand + ((Boolean) rightOperand).toString().toUpperCase();
|
||||
}
|
||||
throw new UnsupportedOperationException(operation.name());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue