Fix SpEL varargs handling and usage of other getValue() methods
Building on the initial work for SPR-12326, this commit addresses three problems: Firstly the ReflectiveMethodResolver is modified to consider a direct parameter match more important than a varargs match. Also in that same type when there are a number of close matches, the first one is taken rather than the last one. Secondly more testcases and better support have been added for the case of passing a single parameter to a varargs accepting method. Finally it is possible to set the root context object indirectly and not pass it on getValue() calls to the expression objects but not all variants of getValue() were handling that. This is now fixed. Issue: SPR-12326
This commit is contained in:
@@ -18,6 +18,7 @@ package org.springframework.expression.spel;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -464,6 +465,35 @@ public class EvaluationTests extends AbstractExpressionTests {
|
||||
assertEquals("T(java.lang.String)", expr.toStringAST());
|
||||
assertEquals(String.class, expr.getValue(Class.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void operatorVariants() throws Exception {
|
||||
SpelExpression expr = (SpelExpression)parser.parseExpression("#a < #b");
|
||||
EvaluationContext ctx = new StandardEvaluationContext();
|
||||
ctx.setVariable("a", (short)3);
|
||||
ctx.setVariable("b", (short)6);
|
||||
assertTrue(expr.getValue(ctx, Boolean.class));
|
||||
ctx.setVariable("b", (byte)6);
|
||||
assertTrue(expr.getValue(ctx, Boolean.class));
|
||||
ctx.setVariable("a", (byte)9);
|
||||
ctx.setVariable("b", (byte)6);
|
||||
assertFalse(expr.getValue(ctx, Boolean.class));
|
||||
ctx.setVariable("a", 10L);
|
||||
ctx.setVariable("b", (short)30);
|
||||
assertTrue(expr.getValue(ctx, Boolean.class));
|
||||
ctx.setVariable("a", (byte)3);
|
||||
ctx.setVariable("b", (short)30);
|
||||
assertTrue(expr.getValue(ctx, Boolean.class));
|
||||
ctx.setVariable("a", (byte)3);
|
||||
ctx.setVariable("b", 30L);
|
||||
assertTrue(expr.getValue(ctx, Boolean.class));
|
||||
ctx.setVariable("a", (byte)3);
|
||||
ctx.setVariable("b", 30f);
|
||||
assertTrue(expr.getValue(ctx, Boolean.class));
|
||||
ctx.setVariable("a", new BigInteger("10"));
|
||||
ctx.setVariable("b", new BigInteger("20"));
|
||||
assertTrue(expr.getValue(ctx, Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeReferencesPrimitive() {
|
||||
|
||||
@@ -1685,6 +1685,21 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
assertEquals(1.0f,expression.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failsWhenSettingContextForExpression_SPR12326() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, this
|
||||
.getClass().getClassLoader()));
|
||||
Person3 person = new Person3("foo", 1);
|
||||
SpelExpression expression = parser.parseRaw("#it?.age?.equals([0])");
|
||||
StandardEvaluationContext context = new StandardEvaluationContext(new Object[] { 1 });
|
||||
context.setVariable("it", person);
|
||||
expression.setEvaluationContext(context);
|
||||
assertTrue(expression.getValue(Boolean.class));
|
||||
assertTrue(expression.getValue(Boolean.class));
|
||||
assertCanCompile(expression);
|
||||
assertTrue(expression.getValue(Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorReference_SPR12326() {
|
||||
String type = this.getClass().getName();
|
||||
@@ -1806,6 +1821,23 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
this.age = age;
|
||||
}
|
||||
}
|
||||
|
||||
public class Person3 {
|
||||
|
||||
private int age;
|
||||
|
||||
public Person3(String name, int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorReference() throws Exception {
|
||||
@@ -1851,6 +1883,57 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
assertCantCompile(expression);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodReferenceReflectiveMethodSelectionWithVarargs() throws Exception {
|
||||
TestClass10 tc = new TestClass10();
|
||||
|
||||
// Should call the non varargs version of concat
|
||||
// (which causes the '::' prefix in test output)
|
||||
expression = parser.parseExpression("concat('test')");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("::test",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("::test",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// This will call the varargs concat with an empty array
|
||||
expression = parser.parseExpression("concat()");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// Should call the non varargs version of concat
|
||||
// (which causes the '::' prefix in test output)
|
||||
expression = parser.parseExpression("concat2('test')");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("::test",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("::test",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// This will call the varargs concat with an empty array
|
||||
expression = parser.parseExpression("concat2()");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("",tc.s);
|
||||
tc.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodReferenceVarargs() throws Exception {
|
||||
TestClass5 tc = new TestClass5();
|
||||
@@ -1866,6 +1949,17 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
assertEquals("",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// varargs string
|
||||
expression = parser.parseExpression("eleven('aaa')");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("aaa",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("aaa",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// varargs string
|
||||
expression = parser.parseExpression("eleven(stringArray)");
|
||||
assertCantCompile(expression);
|
||||
@@ -1898,6 +1992,16 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
expression.getValue(tc);
|
||||
assertEquals(6,tc.i);
|
||||
tc.reset();
|
||||
|
||||
expression = parser.parseExpression("twelve(1)");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals(1,tc.i);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals(1,tc.i);
|
||||
tc.reset();
|
||||
|
||||
// one string then varargs string
|
||||
expression = parser.parseExpression("thirteen('aaa','bbb','ccc')");
|
||||
@@ -1954,6 +2058,16 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
assertEquals("truetruefalse",tc.s);
|
||||
tc.reset();
|
||||
|
||||
expression = parser.parseExpression("arrayz(true)");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("true",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("true",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// varargs short
|
||||
expression = parser.parseExpression("arrays(s1,s2,s3)");
|
||||
assertCantCompile(expression);
|
||||
@@ -1965,6 +2079,16 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
assertEquals("123",tc.s);
|
||||
tc.reset();
|
||||
|
||||
expression = parser.parseExpression("arrays(s1)");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("1",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("1",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// varargs double
|
||||
expression = parser.parseExpression("arrayd(1.0d,2.0d,3.0d)");
|
||||
assertCantCompile(expression);
|
||||
@@ -1976,6 +2100,16 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
assertEquals("1.02.03.0",tc.s);
|
||||
tc.reset();
|
||||
|
||||
expression = parser.parseExpression("arrayd(1.0d)");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("1.0",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("1.0",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// varargs long
|
||||
expression = parser.parseExpression("arrayj(l1,l2,l3)");
|
||||
assertCantCompile(expression);
|
||||
@@ -1986,7 +2120,17 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
expression.getValue(tc);
|
||||
assertEquals("123",tc.s);
|
||||
tc.reset();
|
||||
|
||||
|
||||
expression = parser.parseExpression("arrayj(l1)");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("1",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("1",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// varargs char
|
||||
expression = parser.parseExpression("arrayc(c1,c2,c3)");
|
||||
assertCantCompile(expression);
|
||||
@@ -1997,7 +2141,17 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
expression.getValue(tc);
|
||||
assertEquals("abc",tc.s);
|
||||
tc.reset();
|
||||
|
||||
|
||||
expression = parser.parseExpression("arrayc(c1)");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("a",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("a",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// varargs byte
|
||||
expression = parser.parseExpression("arrayb(b1,b2,b3)");
|
||||
assertCantCompile(expression);
|
||||
@@ -2008,6 +2162,16 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
expression.getValue(tc);
|
||||
assertEquals("656667",tc.s);
|
||||
tc.reset();
|
||||
|
||||
expression = parser.parseExpression("arrayb(b1)");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("65",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("65",tc.s);
|
||||
tc.reset();
|
||||
|
||||
// varargs float
|
||||
expression = parser.parseExpression("arrayf(f1,f2,f3)");
|
||||
@@ -2019,6 +2183,16 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
expression.getValue(tc);
|
||||
assertEquals("1.02.03.0",tc.s);
|
||||
tc.reset();
|
||||
|
||||
expression = parser.parseExpression("arrayf(f1)");
|
||||
assertCantCompile(expression);
|
||||
expression.getValue(tc);
|
||||
assertEquals("1.0",tc.s);
|
||||
assertCanCompile(expression);
|
||||
tc.reset();
|
||||
expression.getValue(tc);
|
||||
assertEquals("1.0",tc.s);
|
||||
tc.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -3307,6 +3481,46 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
public boolean getB() { return b; }
|
||||
}
|
||||
|
||||
public static class TestClass10 {
|
||||
public String s = null;
|
||||
|
||||
public void reset() {
|
||||
s = null;
|
||||
}
|
||||
|
||||
public void concat(String arg) {
|
||||
s = "::"+arg;
|
||||
}
|
||||
|
||||
public void concat(String... vargs) {
|
||||
if (vargs==null) {
|
||||
s = "";
|
||||
}
|
||||
else {
|
||||
s = "";
|
||||
for (String varg: vargs) {
|
||||
s+=varg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void concat2(Object arg) {
|
||||
s = "::"+arg;
|
||||
}
|
||||
|
||||
public void concat2(Object... vargs) {
|
||||
if (vargs==null) {
|
||||
s = "";
|
||||
}
|
||||
else {
|
||||
s = "";
|
||||
for (Object varg: vargs) {
|
||||
s+=varg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestClass5 {
|
||||
public int i = 0;
|
||||
public String s = null;
|
||||
|
||||
Reference in New Issue
Block a user