|
|
|
|
@ -21,13 +21,12 @@ import java.util.List;
@@ -21,13 +21,12 @@ import java.util.List;
|
|
|
|
|
|
|
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
|
|
|
|
|
|
import org.springframework.core.convert.TypeDescriptor; |
|
|
|
|
import org.springframework.expression.ConstructorExecutor; |
|
|
|
|
import org.springframework.expression.ConstructorResolver; |
|
|
|
|
import org.springframework.expression.EvaluationContext; |
|
|
|
|
import org.springframework.expression.Expression; |
|
|
|
|
import org.springframework.expression.spel.standard.SpelExpressionParser; |
|
|
|
|
import org.springframework.expression.spel.support.StandardEvaluationContext; |
|
|
|
|
import org.springframework.expression.spel.support.StandardTypeLocator; |
|
|
|
|
import org.springframework.expression.spel.testresources.Fruit; |
|
|
|
|
import org.springframework.expression.spel.testresources.PlaceOfBirth; |
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
|
@ -41,53 +40,17 @@ import static org.assertj.core.api.Assertions.assertThatException;
@@ -41,53 +40,17 @@ import static org.assertj.core.api.Assertions.assertThatException;
|
|
|
|
|
class ConstructorInvocationTests extends AbstractExpressionTests { |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void testTypeConstructors() { |
|
|
|
|
void constructorWithArgument() { |
|
|
|
|
evaluate("new String('hello world')", "hello world", String.class); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void testNonExistentType() { |
|
|
|
|
void nonExistentType() { |
|
|
|
|
evaluateAndCheckError("new FooBar()", SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("serial") |
|
|
|
|
static class TestException extends Exception { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static class Tester { |
|
|
|
|
|
|
|
|
|
public static int counter; |
|
|
|
|
public int i; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Tester() { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Tester(int i) throws Exception { |
|
|
|
|
counter++; |
|
|
|
|
if (i == 1) { |
|
|
|
|
throw new IllegalArgumentException("IllegalArgumentException for 1"); |
|
|
|
|
} |
|
|
|
|
if (i == 2) { |
|
|
|
|
throw new RuntimeException("RuntimeException for 2"); |
|
|
|
|
} |
|
|
|
|
if (i == 4) { |
|
|
|
|
throw new TestException(); |
|
|
|
|
} |
|
|
|
|
this.i = i; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Tester(PlaceOfBirth pob) { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void testConstructorThrowingException_SPR6760() { |
|
|
|
|
void constructorThrowingException() { |
|
|
|
|
// Test ctor on inventor:
|
|
|
|
|
// On 1 it will throw an IllegalArgumentException
|
|
|
|
|
// On 2 it will throw a RuntimeException
|
|
|
|
|
@ -98,18 +61,18 @@ class ConstructorInvocationTests extends AbstractExpressionTests {
@@ -98,18 +61,18 @@ class ConstructorInvocationTests extends AbstractExpressionTests {
|
|
|
|
|
Expression expr = parser.parseExpression("new org.springframework.expression.spel.ConstructorInvocationTests$Tester(#bar).i"); |
|
|
|
|
|
|
|
|
|
// Normal exit
|
|
|
|
|
StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext(); |
|
|
|
|
eContext.setRootObject(new Tester()); |
|
|
|
|
eContext.setVariable("bar", 3); |
|
|
|
|
Object o = expr.getValue(eContext); |
|
|
|
|
StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); |
|
|
|
|
context.setRootObject(new Tester()); |
|
|
|
|
context.setVariable("bar", 3); |
|
|
|
|
Object o = expr.getValue(context); |
|
|
|
|
assertThat(o).isEqualTo(3); |
|
|
|
|
assertThat(parser.parseExpression("counter").getValue(eContext)).isEqualTo(1); |
|
|
|
|
assertThat(parser.parseExpression("counter").getValue(context)).isEqualTo(1); |
|
|
|
|
|
|
|
|
|
// Now the expression has cached that throwException(int) is the right thing to
|
|
|
|
|
// call. Let's change 'bar' to be a PlaceOfBirth which indicates the cached
|
|
|
|
|
// reference is out of date.
|
|
|
|
|
eContext.setVariable("bar", new PlaceOfBirth("London")); |
|
|
|
|
o = expr.getValue(eContext); |
|
|
|
|
context.setVariable("bar", new PlaceOfBirth("London")); |
|
|
|
|
o = expr.getValue(context); |
|
|
|
|
assertThat(o).isEqualTo(0); |
|
|
|
|
// That confirms the logic to mark the cached reference stale and retry is working
|
|
|
|
|
|
|
|
|
|
@ -117,46 +80,48 @@ class ConstructorInvocationTests extends AbstractExpressionTests {
@@ -117,46 +80,48 @@ class ConstructorInvocationTests extends AbstractExpressionTests {
|
|
|
|
|
// a retry.
|
|
|
|
|
|
|
|
|
|
// First, switch back to throwException(int)
|
|
|
|
|
eContext.setVariable("bar", 3); |
|
|
|
|
o = expr.getValue(eContext); |
|
|
|
|
context.setVariable("bar", 3); |
|
|
|
|
o = expr.getValue(context); |
|
|
|
|
assertThat(o).isEqualTo(3); |
|
|
|
|
assertThat(parser.parseExpression("counter").getValue(eContext)).isEqualTo(2); |
|
|
|
|
assertThat(parser.parseExpression("counter").getValue(context)).isEqualTo(2); |
|
|
|
|
|
|
|
|
|
// 4 will make it throw a checked exception - this will be wrapped by spel on the
|
|
|
|
|
// way out
|
|
|
|
|
eContext.setVariable("bar", 4); |
|
|
|
|
context.setVariable("bar", 4); |
|
|
|
|
assertThatException() |
|
|
|
|
.isThrownBy(() -> expr.getValue(eContext)) |
|
|
|
|
.isThrownBy(() -> expr.getValue(context)) |
|
|
|
|
.withMessageContaining("Tester"); |
|
|
|
|
// A problem occurred whilst attempting to construct an object of type
|
|
|
|
|
// 'org.springframework.expression.spel.ConstructorInvocationTests$Tester'
|
|
|
|
|
// using arguments '(java.lang.Integer)'
|
|
|
|
|
|
|
|
|
|
// If counter is 4 then the method got called twice!
|
|
|
|
|
assertThat(parser.parseExpression("counter").getValue(eContext)).isEqualTo(3); |
|
|
|
|
assertThat(parser.parseExpression("counter").getValue(context)).isEqualTo(3); |
|
|
|
|
|
|
|
|
|
// 1 will make it throw a RuntimeException - SpEL will let this through
|
|
|
|
|
eContext.setVariable("bar", 1); |
|
|
|
|
context.setVariable("bar", 1); |
|
|
|
|
assertThatException() |
|
|
|
|
.isThrownBy(() -> expr.getValue(eContext)) |
|
|
|
|
.isThrownBy(() -> expr.getValue(context)) |
|
|
|
|
.isNotInstanceOf(SpelEvaluationException.class); |
|
|
|
|
// A problem occurred whilst attempting to construct an object of type
|
|
|
|
|
// 'org.springframework.expression.spel.ConstructorInvocationTests$Tester'
|
|
|
|
|
// using arguments '(java.lang.Integer)'
|
|
|
|
|
|
|
|
|
|
// If counter is 5 then the method got called twice!
|
|
|
|
|
assertThat(parser.parseExpression("counter").getValue(eContext)).isEqualTo(4); |
|
|
|
|
assertThat(parser.parseExpression("counter").getValue(context)).isEqualTo(4); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void testAddingConstructorResolvers() { |
|
|
|
|
void constructorResolvers() { |
|
|
|
|
StandardEvaluationContext ctx = new StandardEvaluationContext(); |
|
|
|
|
|
|
|
|
|
// reflective constructor accessor is the only one by default
|
|
|
|
|
List<ConstructorResolver> constructorResolvers = ctx.getConstructorResolvers(); |
|
|
|
|
assertThat(constructorResolvers).hasSize(1); |
|
|
|
|
|
|
|
|
|
ConstructorResolver dummy = new DummyConstructorResolver(); |
|
|
|
|
ConstructorResolver dummy = (context, typeName, argumentTypes) -> { |
|
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
|
}; |
|
|
|
|
ctx.addConstructorResolver(dummy); |
|
|
|
|
assertThat(ctx.getConstructorResolvers()).hasSize(2); |
|
|
|
|
|
|
|
|
|
@ -169,48 +134,29 @@ class ConstructorInvocationTests extends AbstractExpressionTests {
@@ -169,48 +134,29 @@ class ConstructorInvocationTests extends AbstractExpressionTests {
|
|
|
|
|
assertThat(ctx.getConstructorResolvers()).hasSize(2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static class DummyConstructorResolver implements ConstructorResolver { |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public ConstructorExecutor resolve(EvaluationContext context, String typeName, |
|
|
|
|
List<TypeDescriptor> argumentTypes) { |
|
|
|
|
throw new UnsupportedOperationException("Auto-generated method stub"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void testVarargsInvocation01() { |
|
|
|
|
// Calling 'Fruit(String... strings)'
|
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit('a','b','c').stringscount()", 3, |
|
|
|
|
Integer.class); |
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit('a').stringscount()", 1, Integer.class); |
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit().stringscount()", 0, Integer.class); |
|
|
|
|
void varargsConstructors() { |
|
|
|
|
((StandardTypeLocator) super.context.getTypeLocator()).registerImport(Fruit.class.getPackageName()); |
|
|
|
|
|
|
|
|
|
// Calling 'Fruit(String... strings)' - returns length_of_strings
|
|
|
|
|
evaluate("new Fruit('a','b','c').stringscount()", 3, Integer.class); |
|
|
|
|
evaluate("new Fruit('a').stringscount()", 1, Integer.class); |
|
|
|
|
evaluate("new Fruit().stringscount()", 0, Integer.class); |
|
|
|
|
// all need converting to strings
|
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit(1,2,3).stringscount()", 3, Integer.class); |
|
|
|
|
evaluate("new Fruit(1,2,3).stringscount()", 3, Integer.class); |
|
|
|
|
// needs string conversion
|
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit(1).stringscount()", 1, Integer.class); |
|
|
|
|
evaluate("new Fruit(1).stringscount()", 1, Integer.class); |
|
|
|
|
// first and last need conversion
|
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit(1,'a',3.0d).stringscount()", 3, |
|
|
|
|
Integer.class); |
|
|
|
|
} |
|
|
|
|
evaluate("new Fruit(1,'a',3.0d).stringscount()", 3, Integer.class); |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void testVarargsInvocation02() { |
|
|
|
|
// Calling 'Fruit(int i, String... strings)' - returns int + length_of_strings
|
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit(5,'a','b','c').stringscount()", 8, |
|
|
|
|
Integer.class); |
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit(2,'a').stringscount()", 3, Integer.class); |
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit(4).stringscount()", 4, Integer.class); |
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit(8,2,3).stringscount()", 10, Integer.class); |
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit(9).stringscount()", 9, Integer.class); |
|
|
|
|
evaluate("new org.springframework.expression.spel.testresources.Fruit(2,'a',3.0d).stringscount()", 4, |
|
|
|
|
Integer.class); |
|
|
|
|
evaluate( |
|
|
|
|
"new org.springframework.expression.spel.testresources.Fruit(8,stringArrayOfThreeItems).stringscount()", |
|
|
|
|
11, Integer.class); |
|
|
|
|
evaluate("new Fruit(5,'a','b','c').stringscount()", 8, Integer.class); |
|
|
|
|
evaluate("new Fruit(2,'a').stringscount()", 3, Integer.class); |
|
|
|
|
evaluate("new Fruit(4).stringscount()", 4, Integer.class); |
|
|
|
|
evaluate("new Fruit(8,2,3).stringscount()", 10, Integer.class); |
|
|
|
|
evaluate("new Fruit(9).stringscount()", 9, Integer.class); |
|
|
|
|
evaluate("new Fruit(2,'a',3.0d).stringscount()", 4, Integer.class); |
|
|
|
|
evaluate("new Fruit(8,stringArrayOfThreeItems).stringscount()", 11, Integer.class); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
@ -218,7 +164,7 @@ class ConstructorInvocationTests extends AbstractExpressionTests {
@@ -218,7 +164,7 @@ class ConstructorInvocationTests extends AbstractExpressionTests {
|
|
|
|
|
* the argument in order to satisfy a suitable constructor. |
|
|
|
|
*/ |
|
|
|
|
@Test |
|
|
|
|
void testWidening01() { |
|
|
|
|
void widening() { |
|
|
|
|
// widening of int 3 to double 3 is OK
|
|
|
|
|
evaluate("new Double(3)", 3.0d, Double.class); |
|
|
|
|
// widening of int 3 to long 3 is OK
|
|
|
|
|
@ -226,8 +172,40 @@ class ConstructorInvocationTests extends AbstractExpressionTests {
@@ -226,8 +172,40 @@ class ConstructorInvocationTests extends AbstractExpressionTests {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
void testArgumentConversion01() { |
|
|
|
|
void argumentConversion() { |
|
|
|
|
evaluate("new String(3.0d)", "3.0", String.class); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("serial") |
|
|
|
|
static class TestException extends Exception { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static class Tester { |
|
|
|
|
|
|
|
|
|
public static int counter; |
|
|
|
|
public int i; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Tester() { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Tester(int i) throws Exception { |
|
|
|
|
counter++; |
|
|
|
|
if (i == 1) { |
|
|
|
|
throw new IllegalArgumentException("IllegalArgumentException for 1"); |
|
|
|
|
} |
|
|
|
|
if (i == 2) { |
|
|
|
|
throw new RuntimeException("RuntimeException for 2"); |
|
|
|
|
} |
|
|
|
|
if (i == 4) { |
|
|
|
|
throw new TestException(); |
|
|
|
|
} |
|
|
|
|
this.i = i; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Tester(PlaceOfBirth pob) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|