|
|
|
|
@ -18,16 +18,13 @@ package org.springframework.expression.spel;
@@ -18,16 +18,13 @@ package org.springframework.expression.spel;
|
|
|
|
|
|
|
|
|
|
import java.awt.Color; |
|
|
|
|
import java.util.Arrays; |
|
|
|
|
import java.util.HashMap; |
|
|
|
|
import java.util.List; |
|
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
|
|
|
|
|
|
import org.springframework.expression.EvaluationContext; |
|
|
|
|
import org.springframework.expression.EvaluationException; |
|
|
|
|
import org.springframework.expression.Expression; |
|
|
|
|
import org.springframework.expression.ParseException; |
|
|
|
|
import org.springframework.expression.PropertyAccessor; |
|
|
|
|
import org.springframework.expression.TypedValue; |
|
|
|
|
import org.springframework.expression.spel.standard.SpelExpressionParser; |
|
|
|
|
@ -36,10 +33,8 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
@@ -36,10 +33,8 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
|
|
|
|
|
|
|
|
|
///CLOVER:OFF
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Testcases showing the common scenarios/use-cases for picking up the expression language support. |
|
|
|
|
* Test cases showing the common scenarios/use-cases for picking up the expression language support. |
|
|
|
|
* The first test shows very basic usage, just drop it in and go. By 'standard infrastructure', it means:<br> |
|
|
|
|
* <ul> |
|
|
|
|
* <li>The context classloader is used (so, the default classpath) |
|
|
|
|
@ -66,22 +61,17 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
@@ -66,22 +61,17 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
|
|
|
|
|
*/ |
|
|
|
|
@Test |
|
|
|
|
void testScenario_UsingStandardInfrastructure() { |
|
|
|
|
try { |
|
|
|
|
// Create a parser
|
|
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(); |
|
|
|
|
// Parse an expression
|
|
|
|
|
Expression expr = parser.parseRaw("new String('hello world')"); |
|
|
|
|
// Evaluate it using a 'standard' context
|
|
|
|
|
Object value = expr.getValue(); |
|
|
|
|
// They are reusable
|
|
|
|
|
value = expr.getValue(); |
|
|
|
|
|
|
|
|
|
assertThat(value).isEqualTo("hello world"); |
|
|
|
|
assertThat(value.getClass()).isEqualTo(String.class); |
|
|
|
|
} |
|
|
|
|
catch (EvaluationException | ParseException ex) { |
|
|
|
|
throw new AssertionError(ex.getMessage(), ex); |
|
|
|
|
} |
|
|
|
|
// Create a parser
|
|
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(); |
|
|
|
|
// Parse an expression
|
|
|
|
|
Expression expr = parser.parseRaw("new String('hello world')"); |
|
|
|
|
// Evaluate it using a 'standard' context
|
|
|
|
|
Object value = expr.getValue(); |
|
|
|
|
// They are reusable
|
|
|
|
|
value = expr.getValue(); |
|
|
|
|
|
|
|
|
|
assertThat(value).isEqualTo("hello world"); |
|
|
|
|
assertThat(value.getClass()).isEqualTo(String.class); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -95,7 +85,7 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
@@ -95,7 +85,7 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
|
|
|
|
|
StandardEvaluationContext ctx = new StandardEvaluationContext(); |
|
|
|
|
ctx.setVariable("favouriteColour","blue"); |
|
|
|
|
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17); |
|
|
|
|
ctx.setVariable("primes",primes); |
|
|
|
|
ctx.setVariable("primes", primes); |
|
|
|
|
|
|
|
|
|
Expression expr = parser.parseRaw("#favouriteColour"); |
|
|
|
|
Object value = expr.getValue(ctx); |
|
|
|
|
@ -111,14 +101,6 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
@@ -111,14 +101,6 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
|
|
|
|
|
assertThat(value.toString()).isEqualTo("[11, 13, 17]"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static class TestClass { |
|
|
|
|
public String str; |
|
|
|
|
private int property; |
|
|
|
|
public int getProperty() { return property; } |
|
|
|
|
public void setProperty(int i) { property = i; } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Scenario: using your own root context object |
|
|
|
|
*/ |
|
|
|
|
@ -169,61 +151,50 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
@@ -169,61 +151,50 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
|
|
|
|
|
* Scenario: using your own java methods and calling them from the expression |
|
|
|
|
*/ |
|
|
|
|
@Test |
|
|
|
|
void testScenario_RegisteringJavaMethodsAsFunctionsAndCallingThem() throws SecurityException, NoSuchMethodException { |
|
|
|
|
try { |
|
|
|
|
// Create a parser
|
|
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(); |
|
|
|
|
// Use the standard evaluation context
|
|
|
|
|
StandardEvaluationContext ctx = new StandardEvaluationContext(); |
|
|
|
|
ctx.registerFunction("repeat",ExpressionLanguageScenarioTests.class.getDeclaredMethod("repeat",String.class)); |
|
|
|
|
|
|
|
|
|
Expression expr = parser.parseRaw("#repeat('hello')"); |
|
|
|
|
Object value = expr.getValue(ctx); |
|
|
|
|
assertThat(value).isEqualTo("hellohello"); |
|
|
|
|
void testScenario_RegisteringJavaMethodsAsFunctionsAndCallingThem() throws Exception { |
|
|
|
|
// Create a parser
|
|
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(); |
|
|
|
|
// Use the standard evaluation context
|
|
|
|
|
StandardEvaluationContext ctx = new StandardEvaluationContext(); |
|
|
|
|
ctx.registerFunction("repeat", ExpressionLanguageScenarioTests.class.getDeclaredMethod("repeat", String.class)); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
catch (EvaluationException | ParseException ex) { |
|
|
|
|
throw new AssertionError(ex.getMessage(), ex); |
|
|
|
|
} |
|
|
|
|
Expression expr = parser.parseRaw("#repeat('hello')"); |
|
|
|
|
Object value = expr.getValue(ctx); |
|
|
|
|
assertThat(value).isEqualTo("hellohello"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Scenario: looking up your own MethodHandles and calling them from the expression |
|
|
|
|
*/ |
|
|
|
|
@Test |
|
|
|
|
void testScenario_RegisteringJavaMethodsAsMethodHandlesAndCallingThem() throws SecurityException { |
|
|
|
|
try { |
|
|
|
|
// Create a parser
|
|
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(); |
|
|
|
|
//this.context is already populated with all relevant MethodHandle examples
|
|
|
|
|
|
|
|
|
|
Expression expr = parser.parseRaw("#message('Message with %s words: <%s>', 2, 'Hello World', 'ignored')"); |
|
|
|
|
Object value = expr.getValue(this.context); |
|
|
|
|
assertThat(value).isEqualTo("Message with 2 words: <Hello World>"); |
|
|
|
|
|
|
|
|
|
expr = parser.parseRaw("#messageTemplate('bound', 2, 'Hello World', 'ignored')"); |
|
|
|
|
value = expr.getValue(this.context); |
|
|
|
|
assertThat(value).isEqualTo("This is a bound message with 2 words: <Hello World>"); |
|
|
|
|
|
|
|
|
|
expr = parser.parseRaw("#messageBound()"); |
|
|
|
|
value = expr.getValue(this.context); |
|
|
|
|
assertThat(value).isEqualTo("This is a prerecorded message with 3 words: <Oh Hello World>"); |
|
|
|
|
|
|
|
|
|
Expression staticExpr = parser.parseRaw("#messageStatic('Message with %s words: <%s>', 2, 'Hello World', 'ignored')"); |
|
|
|
|
Object staticValue = staticExpr.getValue(this.context); |
|
|
|
|
assertThat(staticValue).isEqualTo("Message with 2 words: <Hello World>"); |
|
|
|
|
|
|
|
|
|
staticExpr = parser.parseRaw("#messageStaticTemplate('bound', 2, 'Hello World', 'ignored')"); |
|
|
|
|
staticValue = staticExpr.getValue(this.context); |
|
|
|
|
assertThat(staticValue).isEqualTo("This is a bound message with 2 words: <Hello World>"); |
|
|
|
|
|
|
|
|
|
staticExpr = parser.parseRaw("#messageStaticBound()"); |
|
|
|
|
staticValue = staticExpr.getValue(this.context); |
|
|
|
|
assertThat(staticValue).isEqualTo("This is a prerecorded message with 3 words: <Oh Hello World>"); |
|
|
|
|
} |
|
|
|
|
catch (EvaluationException | ParseException ex) { |
|
|
|
|
throw new AssertionError(ex.getMessage(), ex); |
|
|
|
|
} |
|
|
|
|
void testScenario_RegisteringJavaMethodsAsMethodHandlesAndCallingThem() throws Exception { |
|
|
|
|
// Create a parser
|
|
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(); |
|
|
|
|
//this.context is already populated with all relevant MethodHandle examples
|
|
|
|
|
|
|
|
|
|
Expression expr = parser.parseRaw("#message('Message with %s words: <%s>', 2, 'Hello World', 'ignored')"); |
|
|
|
|
Object value = expr.getValue(this.context); |
|
|
|
|
assertThat(value).isEqualTo("Message with 2 words: <Hello World>"); |
|
|
|
|
|
|
|
|
|
expr = parser.parseRaw("#messageTemplate('bound', 2, 'Hello World', 'ignored')"); |
|
|
|
|
value = expr.getValue(this.context); |
|
|
|
|
assertThat(value).isEqualTo("This is a bound message with 2 words: <Hello World>"); |
|
|
|
|
|
|
|
|
|
expr = parser.parseRaw("#messageBound()"); |
|
|
|
|
value = expr.getValue(this.context); |
|
|
|
|
assertThat(value).isEqualTo("This is a prerecorded message with 3 words: <Oh Hello World>"); |
|
|
|
|
|
|
|
|
|
Expression staticExpr = parser.parseRaw("#messageStatic('Message with %s words: <%s>', 2, 'Hello World', 'ignored')"); |
|
|
|
|
Object staticValue = staticExpr.getValue(this.context); |
|
|
|
|
assertThat(staticValue).isEqualTo("Message with 2 words: <Hello World>"); |
|
|
|
|
|
|
|
|
|
staticExpr = parser.parseRaw("#messageStaticTemplate('bound', 2, 'Hello World', 'ignored')"); |
|
|
|
|
staticValue = staticExpr.getValue(this.context); |
|
|
|
|
assertThat(staticValue).isEqualTo("This is a bound message with 2 words: <Hello World>"); |
|
|
|
|
|
|
|
|
|
staticExpr = parser.parseRaw("#messageStaticBound()"); |
|
|
|
|
staticValue = staticExpr.getValue(this.context); |
|
|
|
|
assertThat(staticValue).isEqualTo("This is a prerecorded message with 3 words: <Oh Hello World>"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -240,8 +211,8 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
@@ -240,8 +211,8 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
|
|
|
|
|
Expression expr = parser.parseRaw("orange"); |
|
|
|
|
Object value = expr.getValue(ctx); |
|
|
|
|
assertThat(value).isEqualTo(Color.orange); |
|
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
|
expr.setValue(ctx, Color.blue)) |
|
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class) |
|
|
|
|
.isThrownBy(() -> expr.setValue(ctx, Color.blue)) |
|
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -257,25 +228,31 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
@@ -257,25 +228,31 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
|
|
|
|
|
Object value = expr.getValue(ctx); |
|
|
|
|
assertThat(value).isEqualTo(Color.green); |
|
|
|
|
|
|
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
|
expr.setValue(ctx, Color.blue)) |
|
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class) |
|
|
|
|
.isThrownBy(() -> expr.setValue(ctx, Color.blue)) |
|
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static class TestClass { |
|
|
|
|
public String str; |
|
|
|
|
private int property; |
|
|
|
|
public int getProperty() { return property; } |
|
|
|
|
public void setProperty(int i) { property = i; } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Regardless of the current context object, or root context object, this resolver can tell you what colour a fruit is ! |
|
|
|
|
* It only supports property reading, not writing. To support writing it would need to override canWrite() and write() |
|
|
|
|
*/ |
|
|
|
|
private static class FruitColourAccessor implements PropertyAccessor { |
|
|
|
|
|
|
|
|
|
private static Map<String,Color> propertyMap = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
static { |
|
|
|
|
propertyMap.put("banana",Color.yellow); |
|
|
|
|
propertyMap.put("apple",Color.red); |
|
|
|
|
propertyMap.put("orange",Color.orange); |
|
|
|
|
} |
|
|
|
|
private static final Map<String,Color> propertyMap = Map.of( |
|
|
|
|
"banana", Color.yellow, |
|
|
|
|
"apple", Color.red, |
|
|
|
|
"orange", Color.orange |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Null means you might be able to read any property, if an earlier property resolver hasn't beaten you to it |
|
|
|
|
@ -303,7 +280,6 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
@@ -303,7 +280,6 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
|
|
|
|
|
@Override |
|
|
|
|
public void write(EvaluationContext context, Object target, String name, Object newValue) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -313,12 +289,10 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
@@ -313,12 +289,10 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
|
|
|
|
|
*/ |
|
|
|
|
private static class VegetableColourAccessor implements PropertyAccessor { |
|
|
|
|
|
|
|
|
|
private static Map<String,Color> propertyMap = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
static { |
|
|
|
|
propertyMap.put("carrot",Color.orange); |
|
|
|
|
propertyMap.put("pea",Color.green); |
|
|
|
|
} |
|
|
|
|
private static Map<String,Color> propertyMap = Map.of( |
|
|
|
|
"carrot", Color.orange, |
|
|
|
|
"pea", Color.green |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Null means you might be able to read any property, if an earlier property resolver hasn't beaten you to it |
|
|
|
|
@ -346,7 +320,6 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
@@ -346,7 +320,6 @@ class ExpressionLanguageScenarioTests extends AbstractExpressionTests {
|
|
|
|
|
@Override |
|
|
|
|
public void write(EvaluationContext context, Object target, String name, Object newValue) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|