Polishing

This commit is contained in:
Sam Brannen
2024-02-10 16:56:02 +01:00
parent 64fc9ee301
commit 6b67972ec4

View File

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