/* * Copyright 2014 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.expression.spel; import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.junit.Test; import org.springframework.asm.MethodVisitor; import org.springframework.expression.AccessException; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.TypedValue; import org.springframework.expression.spel.ast.CompoundExpression; import org.springframework.expression.spel.ast.OpLT; import org.springframework.expression.spel.ast.SpelNodeImpl; import org.springframework.expression.spel.ast.Ternary; import org.springframework.expression.spel.standard.SpelCompiler; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import static org.junit.Assert.*; /** * Checks the behaviour of the SpelCompiler. This should cover compilation all compiled node types. * * @author Andy Clement * @since 4.1 */ public class SpelCompilationCoverageTests extends AbstractExpressionTests { private Expression expression; private SpelNodeImpl ast; /* * Further TODOs for compilation: * * - OpMinus with a single literal operand could be treated as a negative literal. Will save a * pointless loading of 0 and then a subtract instruction in code gen. * - allow other accessors/resolvers to participate in compilation and create their own code * - A TypeReference followed by (what ends up as) a static method invocation can really skip * code gen for the TypeReference since once that is used to locate the method it is not * used again. * - The opEq implementation is quite basic. It will compare numbers of the same type (allowing * them to be their boxed or unboxed variants) or compare object references. It does not * compile expressions where numbers are of different types or when objects implement * Comparable. * * Compiled nodes: * * TypeReference * OperatorInstanceOf * StringLiteral * NullLiteral * RealLiteral * IntLiteral * LongLiteral * BooleanLiteral * FloatLiteral * OpOr * OpAnd * OperatorNot * Ternary * Elvis * VariableReference * OpLt * OpLe * OpGt * OpGe * OpEq * OpNe * OpPlus * OpMinus * OpMultiply * OpDivide * MethodReference * PropertyOrFieldReference * Indexer * CompoundExpression * ConstructorReference * FunctionReference * * Not yet compiled (some may never need to be): * Assign * BeanReference * Identifier * InlineList * OpDec * OpBetween * OpMatches * OpPower * OpInc * OpModulus * Projection * QualifiedId * Selection */ @Test public void typeReference() throws Exception { expression = parse("T(String)"); assertEquals(String.class,expression.getValue()); assertCanCompile(expression); assertEquals(String.class,expression.getValue()); expression = parse("T(java.io.IOException)"); assertEquals(IOException.class,expression.getValue()); assertCanCompile(expression); assertEquals(IOException.class,expression.getValue()); expression = parse("T(java.io.IOException[])"); assertEquals(IOException[].class,expression.getValue()); assertCanCompile(expression); assertEquals(IOException[].class,expression.getValue()); expression = parse("T(int[][])"); assertEquals(int[][].class,expression.getValue()); assertCanCompile(expression); assertEquals(int[][].class,expression.getValue()); expression = parse("T(int)"); assertEquals(Integer.TYPE,expression.getValue()); assertCanCompile(expression); assertEquals(Integer.TYPE,expression.getValue()); expression = parse("T(byte)"); assertEquals(Byte.TYPE,expression.getValue()); assertCanCompile(expression); assertEquals(Byte.TYPE,expression.getValue()); expression = parse("T(char)"); assertEquals(Character.TYPE,expression.getValue()); assertCanCompile(expression); assertEquals(Character.TYPE,expression.getValue()); expression = parse("T(short)"); assertEquals(Short.TYPE,expression.getValue()); assertCanCompile(expression); assertEquals(Short.TYPE,expression.getValue()); expression = parse("T(long)"); assertEquals(Long.TYPE,expression.getValue()); assertCanCompile(expression); assertEquals(Long.TYPE,expression.getValue()); expression = parse("T(float)"); assertEquals(Float.TYPE,expression.getValue()); assertCanCompile(expression); assertEquals(Float.TYPE,expression.getValue()); expression = parse("T(double)"); assertEquals(Double.TYPE,expression.getValue()); assertCanCompile(expression); assertEquals(Double.TYPE,expression.getValue()); expression = parse("T(boolean)"); assertEquals(Boolean.TYPE,expression.getValue()); assertCanCompile(expression); assertEquals(Boolean.TYPE,expression.getValue()); expression = parse("T(Missing)"); assertGetValueFail(expression); assertCantCompile(expression); } @SuppressWarnings("unchecked") @Test public void operatorInstanceOf() throws Exception { expression = parse("'xyz' instanceof T(String)"); assertEquals(true,expression.getValue()); assertCanCompile(expression); assertEquals(true,expression.getValue()); expression = parse("'xyz' instanceof T(Integer)"); assertEquals(false,expression.getValue()); assertCanCompile(expression); assertEquals(false,expression.getValue()); List list = new ArrayList(); expression = parse("#root instanceof T(java.util.List)"); assertEquals(true,expression.getValue(list)); assertCanCompile(expression); assertEquals(true,expression.getValue(list)); List[] arrayOfLists = new List[]{new ArrayList()}; expression = parse("#root instanceof T(java.util.List[])"); assertEquals(true,expression.getValue(arrayOfLists)); assertCanCompile(expression); assertEquals(true,expression.getValue(arrayOfLists)); int[] intArray = new int[]{1,2,3}; expression = parse("#root instanceof T(int[])"); assertEquals(true,expression.getValue(intArray)); assertCanCompile(expression); assertEquals(true,expression.getValue(intArray)); String root = null; expression = parse("#root instanceof T(Integer)"); assertEquals(false,expression.getValue(root)); assertCanCompile(expression); assertEquals(false,expression.getValue(root)); // root still null expression = parse("#root instanceof T(java.lang.Object)"); assertEquals(false,expression.getValue(root)); assertCanCompile(expression); assertEquals(false,expression.getValue(root)); root = "howdy!"; expression = parse("#root instanceof T(java.lang.Object)"); assertEquals(true,expression.getValue(root)); assertCanCompile(expression); assertEquals(true,expression.getValue(root)); } @Test public void stringLiteral() throws Exception { expression = parser.parseExpression("'abcde'"); assertEquals("abcde",expression.getValue(new TestClass1(),String.class)); assertCanCompile(expression); String resultC = expression.getValue(new TestClass1(),String.class); assertEquals("abcde",resultC); assertEquals("abcde",expression.getValue(String.class)); assertEquals("abcde",expression.getValue()); assertEquals("abcde",expression.getValue(new StandardEvaluationContext())); expression = parser.parseExpression("\"abcde\""); assertCanCompile(expression); assertEquals("abcde",expression.getValue(String.class)); } @Test public void nullLiteral() throws Exception { expression = parser.parseExpression("null"); Object resultI = expression.getValue(new TestClass1(),Object.class); assertCanCompile(expression); Object resultC = expression.getValue(new TestClass1(),Object.class); assertEquals(null,resultI); assertEquals(null,resultC); } @Test public void realLiteral() throws Exception { expression = parser.parseExpression("3.4d"); double resultI = expression.getValue(new TestClass1(),Double.TYPE); assertCanCompile(expression); double resultC = expression.getValue(new TestClass1(),Double.TYPE); assertEquals(3.4d,resultI,0.1d); assertEquals(3.4d,resultC,0.1d); assertEquals(3.4d,expression.getValue()); } @Test public void intLiteral() throws Exception { expression = parser.parseExpression("42"); int resultI = expression.getValue(new TestClass1(),Integer.TYPE); assertCanCompile(expression); int resultC = expression.getValue(new TestClass1(),Integer.TYPE); assertEquals(42,resultI); assertEquals(42,resultC); expression = parser.parseExpression("T(Integer).valueOf(42)"); expression.getValue(Integer.class); assertCanCompile(expression); assertEquals(new Integer(42),expression.getValue(null,Integer.class)); // Code gen is different for -1 .. 6 because there are bytecode instructions specifically for those // values // Not an int literal but an opminus with one operand: // expression = parser.parseExpression("-1"); // assertCanCompile(expression); // assertEquals(-1,expression.getValue()); expression = parser.parseExpression("0"); assertCanCompile(expression); assertEquals(0,expression.getValue()); expression = parser.parseExpression("2"); assertCanCompile(expression); assertEquals(2,expression.getValue()); expression = parser.parseExpression("7"); assertCanCompile(expression); assertEquals(7,expression.getValue()); } @Test public void longLiteral() throws Exception { expression = parser.parseExpression("99L"); long resultI = expression.getValue(new TestClass1(),Long.TYPE); assertCanCompile(expression); long resultC = expression.getValue(new TestClass1(),Long.TYPE); assertEquals(99L,resultI); assertEquals(99L,resultC); } @Test public void booleanLiteral() throws Exception { expression = parser.parseExpression("true"); boolean resultI = expression.getValue(1,Boolean.TYPE); assertEquals(true,resultI); assertTrue(SpelCompiler.compile(expression)); boolean resultC = expression.getValue(1,Boolean.TYPE); assertEquals(true,resultC); expression = parser.parseExpression("false"); resultI = expression.getValue(1,Boolean.TYPE); assertEquals(false,resultI); assertTrue(SpelCompiler.compile(expression)); resultC = expression.getValue(1,Boolean.TYPE); assertEquals(false,resultC); } @Test public void floatLiteral() throws Exception { expression = parser.parseExpression("3.4f"); float resultI = expression.getValue(new TestClass1(),Float.TYPE); assertCanCompile(expression); float resultC = expression.getValue(new TestClass1(),Float.TYPE); assertEquals(3.4f,resultI,0.1f); assertEquals(3.4f,resultC,0.1f); assertEquals(3.4f,expression.getValue()); } @Test public void opOr() throws Exception { Expression expression = parser.parseExpression("false or false"); boolean resultI = expression.getValue(1,Boolean.TYPE); SpelCompiler.compile(expression); boolean resultC = expression.getValue(1,Boolean.TYPE); assertEquals(false,resultI); assertEquals(false,resultC); expression = parser.parseExpression("false or true"); resultI = expression.getValue(1,Boolean.TYPE); assertCanCompile(expression); resultC = expression.getValue(1,Boolean.TYPE); assertEquals(true,resultI); assertEquals(true,resultC); expression = parser.parseExpression("true or false"); resultI = expression.getValue(1,Boolean.TYPE); assertCanCompile(expression); resultC = expression.getValue(1,Boolean.TYPE); assertEquals(true,resultI); assertEquals(true,resultC); expression = parser.parseExpression("true or true"); resultI = expression.getValue(1,Boolean.TYPE); assertCanCompile(expression); resultC = expression.getValue(1,Boolean.TYPE); assertEquals(true,resultI); assertEquals(true,resultC); TestClass4 tc = new TestClass4(); expression = parser.parseExpression("getfalse() or gettrue()"); resultI = expression.getValue(tc,Boolean.TYPE); assertCanCompile(expression); resultC = expression.getValue(tc,Boolean.TYPE); assertEquals(true,resultI); assertEquals(true,resultC); // Can't compile this as we aren't going down the getfalse() branch in our evaluation expression = parser.parseExpression("gettrue() or getfalse()"); resultI = expression.getValue(tc,Boolean.TYPE); assertCantCompile(expression); expression = parser.parseExpression("getA() or getB()"); tc.a = true; tc.b = true; resultI = expression.getValue(tc,Boolean.TYPE); assertCantCompile(expression); // Haven't yet been into second branch tc.a = false; tc.b = true; resultI = expression.getValue(tc,Boolean.TYPE); assertCanCompile(expression); // Now been down both assertTrue(resultI); boolean b = false; expression = parse("#root or #root"); Object resultI2 = expression.getValue(b); assertCanCompile(expression); assertFalse((Boolean)resultI2); assertFalse((Boolean)expression.getValue(b)); } @Test public void opAnd() throws Exception { Expression expression = parser.parseExpression("false and false"); boolean resultI = expression.getValue(1,Boolean.TYPE); SpelCompiler.compile(expression); boolean resultC = expression.getValue(1,Boolean.TYPE); assertEquals(false,resultI); assertEquals(false,resultC); expression = parser.parseExpression("false and true"); resultI = expression.getValue(1,Boolean.TYPE); SpelCompiler.compile(expression); resultC = expression.getValue(1,Boolean.TYPE); assertEquals(false,resultI); assertEquals(false,resultC); expression = parser.parseExpression("true and false"); resultI = expression.getValue(1,Boolean.TYPE); SpelCompiler.compile(expression); resultC = expression.getValue(1,Boolean.TYPE); assertEquals(false,resultI); assertEquals(false,resultC); expression = parser.parseExpression("true and true"); resultI = expression.getValue(1,Boolean.TYPE); SpelCompiler.compile(expression); resultC = expression.getValue(1,Boolean.TYPE); assertEquals(true,resultI); assertEquals(true,resultC); TestClass4 tc = new TestClass4(); // Can't compile this as we aren't going down the gettrue() branch in our evaluation expression = parser.parseExpression("getfalse() and gettrue()"); resultI = expression.getValue(tc,Boolean.TYPE); assertCantCompile(expression); expression = parser.parseExpression("getA() and getB()"); tc.a = false; tc.b = false; resultI = expression.getValue(tc,Boolean.TYPE); assertCantCompile(expression); // Haven't yet been into second branch tc.a = true; tc.b = false; resultI = expression.getValue(tc,Boolean.TYPE); assertCanCompile(expression); // Now been down both assertFalse(resultI); tc.a = true; tc.b = true; resultI = expression.getValue(tc,Boolean.TYPE); assertTrue(resultI); boolean b = true; expression = parse("#root and #root"); Object resultI2 = expression.getValue(b); assertCanCompile(expression); assertTrue((Boolean)resultI2); assertTrue((Boolean)expression.getValue(b)); } @Test public void operatorNot() throws Exception { expression = parse("!true"); assertEquals(false,expression.getValue()); assertCanCompile(expression); assertEquals(false,expression.getValue()); expression = parse("!false"); assertEquals(true,expression.getValue()); assertCanCompile(expression); assertEquals(true,expression.getValue()); boolean b = true; expression = parse("!#root"); assertEquals(false,expression.getValue(b)); assertCanCompile(expression); assertEquals(false,expression.getValue(b)); b = false; expression = parse("!#root"); assertEquals(true,expression.getValue(b)); assertCanCompile(expression); assertEquals(true,expression.getValue(b)); } @Test public void ternary() throws Exception { Expression expression = parser.parseExpression("true?'a':'b'"); String resultI = expression.getValue(String.class); assertCanCompile(expression); String resultC = expression.getValue(String.class); assertEquals("a",resultI); assertEquals("a",resultC); expression = parser.parseExpression("false?'a':'b'"); resultI = expression.getValue(String.class); assertCanCompile(expression); resultC = expression.getValue(String.class); assertEquals("b",resultI); assertEquals("b",resultC); expression = parser.parseExpression("false?1:'b'"); // All literals so we can do this straight away assertCanCompile(expression); assertEquals("b",expression.getValue()); boolean root = true; expression = parser.parseExpression("(#root and true)?T(Integer).valueOf(1):T(Long).valueOf(3L)"); assertEquals(1,expression.getValue(root)); assertCantCompile(expression); // Have not gone down false branch root = false; assertEquals(3L,expression.getValue(root)); assertCanCompile(expression); assertEquals(3L,expression.getValue(root)); root = true; assertEquals(1,expression.getValue(root)); } @Test public void elvis() throws Exception { Expression expression = parser.parseExpression("'a'?:'b'"); String resultI = expression.getValue(String.class); assertCanCompile(expression); String resultC = expression.getValue(String.class); assertEquals("a",resultI); assertEquals("a",resultC); expression = parser.parseExpression("null?:'a'"); resultI = expression.getValue(String.class); assertCanCompile(expression); resultC = expression.getValue(String.class); assertEquals("a",resultI); assertEquals("a",resultC); String s = "abc"; expression = parser.parseExpression("#root?:'b'"); assertCantCompile(expression); resultI = expression.getValue(s,String.class); assertEquals("abc",resultI); assertCanCompile(expression); } @Test public void variableReference_root() throws Exception { String s = "hello"; Expression expression = parser.parseExpression("#root"); String resultI = expression.getValue(s,String.class); assertCanCompile(expression); String resultC = expression.getValue(s,String.class); assertEquals(s,resultI); assertEquals(s,resultC); expression = parser.parseExpression("#root"); int i = (Integer)expression.getValue(42); assertEquals(42,i); assertCanCompile(expression); i = (Integer)expression.getValue(42); assertEquals(42,i); } public static String concat(String a, String b) { return a+b; } @Test public void functionReference() throws Exception { EvaluationContext ctx = new StandardEvaluationContext(); Method m = this.getClass().getDeclaredMethod("concat",String.class,String.class); ctx.setVariable("concat",m); expression = parser.parseExpression("#concat('a','b')"); assertEquals("ab",expression.getValue(ctx)); assertCanCompile(expression); assertEquals("ab",expression.getValue(ctx)); expression = parser.parseExpression("#concat(#concat('a','b'),'c').charAt(1)"); assertEquals('b',expression.getValue(ctx)); assertCanCompile(expression); assertEquals('b',expression.getValue(ctx)); expression = parser.parseExpression("#concat(#a,#b)"); ctx.setVariable("a", "foo"); ctx.setVariable("b", "bar"); assertEquals("foobar",expression.getValue(ctx)); assertCanCompile(expression); assertEquals("foobar",expression.getValue(ctx)); ctx.setVariable("b", "boo"); assertEquals("fooboo",expression.getValue(ctx)); } @Test public void variableReference_userDefined() throws Exception { EvaluationContext ctx = new StandardEvaluationContext(); ctx.setVariable("target", "abc"); expression = parser.parseExpression("#target"); assertEquals("abc",expression.getValue(ctx)); assertCanCompile(expression); assertEquals("abc",expression.getValue(ctx)); ctx.setVariable("target", "123"); assertEquals("123",expression.getValue(ctx)); ctx.setVariable("target", 42); try { assertEquals(42,expression.getValue(ctx)); fail(); } catch (SpelEvaluationException see) { assertTrue(see.getCause() instanceof ClassCastException); } ctx.setVariable("target", "abc"); expression = parser.parseExpression("#target.charAt(0)"); assertEquals('a',expression.getValue(ctx)); assertCanCompile(expression); assertEquals('a',expression.getValue(ctx)); ctx.setVariable("target", "1"); assertEquals('1',expression.getValue(ctx)); ctx.setVariable("target", 42); try { assertEquals('4',expression.getValue(ctx)); fail(); } catch (SpelEvaluationException see) { assertTrue(see.getCause() instanceof ClassCastException); } } @Test public void opLt() throws Exception { expression = parse("3.0d < 4.0d"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3446.0d < 1123.0d"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3 < 1"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("2 < 4"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3.0f < 1.0f"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("1.0f < 5.0f"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("30L < 30L"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("15L < 20L"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); // Differing types of number, not yet supported expression = parse("1 < 3.0d"); assertCantCompile(expression); expression = parse("T(Integer).valueOf(3) < 4"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("T(Integer).valueOf(3) < T(Integer).valueOf(3)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("5 < T(Integer).valueOf(3)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); } @Test public void opLe() throws Exception { expression = parse("3.0d <= 4.0d"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3446.0d <= 1123.0d"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3446.0d <= 3446.0d"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3 <= 1"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("2 <= 4"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3 <= 3"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3.0f <= 1.0f"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("1.0f <= 5.0f"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("2.0f <= 2.0f"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("30L <= 30L"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("15L <= 20L"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); // Differing types of number, not yet supported expression = parse("1 <= 3.0d"); assertCantCompile(expression); expression = parse("T(Integer).valueOf(3) <= 4"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("T(Integer).valueOf(3) <= T(Integer).valueOf(3)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("5 <= T(Integer).valueOf(3)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); } @Test public void opGt() throws Exception { expression = parse("3.0d > 4.0d"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3446.0d > 1123.0d"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3 > 1"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("2 > 4"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3.0f > 1.0f"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("1.0f > 5.0f"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("30L > 30L"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("15L > 20L"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); // Differing types of number, not yet supported expression = parse("1 > 3.0d"); assertCantCompile(expression); expression = parse("T(Integer).valueOf(3) > 4"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Integer).valueOf(3) > T(Integer).valueOf(3)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("5 > T(Integer).valueOf(3)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); } @Test public void opGe() throws Exception { expression = parse("3.0d >= 4.0d"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3446.0d >= 1123.0d"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3446.0d >= 3446.0d"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3 >= 1"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("2 >= 4"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3 >= 3"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3.0f >= 1.0f"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("1.0f >= 5.0f"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3.0f >= 3.0f"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("40L >= 30L"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("15L >= 20L"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("30L >= 30L"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); // Differing types of number, not yet supported expression = parse("1 >= 3.0d"); assertCantCompile(expression); expression = parse("T(Integer).valueOf(3) >= 4"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Integer).valueOf(3) >= T(Integer).valueOf(3)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("5 >= T(Integer).valueOf(3)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); } @Test public void opEq() throws Exception { TestClass7 tc7 = new TestClass7(); expression = parse("property == 'UK'"); assertTrue((Boolean)expression.getValue(tc7)); TestClass7.property = null; assertFalse((Boolean)expression.getValue(tc7)); assertCanCompile(expression); TestClass7.reset(); assertTrue((Boolean)expression.getValue(tc7)); TestClass7.property = "UK"; assertTrue((Boolean)expression.getValue(tc7)); TestClass7.reset(); TestClass7.property = null; assertFalse((Boolean)expression.getValue(tc7)); expression = parse("property == null"); assertTrue((Boolean)expression.getValue(tc7)); assertCanCompile(expression); assertTrue((Boolean)expression.getValue(tc7)); expression = parse("3.0d == 4.0d"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3446.0d == 3446.0d"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3 == 1"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3 == 3"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3.0f == 1.0f"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("2.0f == 2.0f"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("30L == 30L"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("15L == 20L"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); // number types are not the same expression = parse("1 == 3.0d"); assertCantCompile(expression); Double d = 3.0d; expression = parse("#root==3.0d"); assertTrue((Boolean)expression.getValue(d)); assertCanCompile(expression); assertTrue((Boolean)expression.getValue(d)); Integer i = 3; expression = parse("#root==3"); assertTrue((Boolean)expression.getValue(i)); assertCanCompile(expression); assertTrue((Boolean)expression.getValue(i)); Float f = 3.0f; expression = parse("#root==3.0f"); assertTrue((Boolean)expression.getValue(f)); assertCanCompile(expression); assertTrue((Boolean)expression.getValue(f)); long l = 300l; expression = parse("#root==300l"); assertTrue((Boolean)expression.getValue(l)); assertCanCompile(expression); assertTrue((Boolean)expression.getValue(l)); boolean b = true; expression = parse("#root==true"); assertTrue((Boolean)expression.getValue(b)); assertCanCompile(expression); assertTrue((Boolean)expression.getValue(b)); expression = parse("T(Integer).valueOf(3) == 4"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Integer).valueOf(3) == T(Integer).valueOf(3)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("5 == T(Integer).valueOf(3)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Float).valueOf(3.0f) == 4.0f"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Float).valueOf(3.0f) == T(Float).valueOf(3.0f)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("5.0f == T(Float).valueOf(3.0f)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Long).valueOf(3L) == 4L"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Long).valueOf(3L) == T(Long).valueOf(3L)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("5L == T(Long).valueOf(3L)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Double).valueOf(3.0d) == 4.0d"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Double).valueOf(3.0d) == T(Double).valueOf(3.0d)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("5.0d == T(Double).valueOf(3.0d)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("false == true"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Boolean).valueOf('true') == T(Boolean).valueOf('true')"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("T(Boolean).valueOf('true') == true"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("false == T(Boolean).valueOf('false')"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); } @Test public void opNe() throws Exception { expression = parse("3.0d != 4.0d"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3446.0d != 3446.0d"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3 != 1"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("3 != 3"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("3.0f != 1.0f"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("2.0f != 2.0f"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("30L != 30L"); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("15L != 20L"); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); // not compatible number types expression = parse("1 != 3.0d"); assertCantCompile(expression); expression = parse("T(Integer).valueOf(3) != 4"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("T(Integer).valueOf(3) != T(Integer).valueOf(3)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("5 != T(Integer).valueOf(3)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("T(Float).valueOf(3.0f) != 4.0f"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("T(Float).valueOf(3.0f) != T(Float).valueOf(3.0f)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("5.0f != T(Float).valueOf(3.0f)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("T(Long).valueOf(3L) != 4L"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("T(Long).valueOf(3L) != T(Long).valueOf(3L)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("5L != T(Long).valueOf(3L)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("T(Double).valueOf(3.0d) == 4.0d"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Double).valueOf(3.0d) == T(Double).valueOf(3.0d)"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("5.0d == T(Double).valueOf(3.0d)"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("false == true"); assertFalse((Boolean)expression.getValue()); assertCanCompile(expression); assertFalse((Boolean)expression.getValue()); expression = parse("T(Boolean).valueOf('true') == T(Boolean).valueOf('true')"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("T(Boolean).valueOf('true') == true"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); expression = parse("false == T(Boolean).valueOf('false')"); assertTrue((Boolean)expression.getValue()); assertCanCompile(expression); assertTrue((Boolean)expression.getValue()); } @Test public void opPlus() throws Exception { expression = parse("2+2"); expression.getValue(); assertCanCompile(expression); assertEquals(4,expression.getValue()); expression = parse("2L+2L"); expression.getValue(); assertCanCompile(expression); assertEquals(4L,expression.getValue()); expression = parse("2.0f+2.0f"); expression.getValue(); assertCanCompile(expression); assertEquals(4.0f,expression.getValue()); expression = parse("3.0d+4.0d"); expression.getValue(); assertCanCompile(expression); assertEquals(7.0d,expression.getValue()); expression = parse("+1"); expression.getValue(); assertCanCompile(expression); assertEquals(1,expression.getValue()); expression = parse("+1L"); expression.getValue(); assertCanCompile(expression); assertEquals(1L,expression.getValue()); expression = parse("+1.5f"); expression.getValue(); assertCanCompile(expression); assertEquals(1.5f,expression.getValue()); expression = parse("+2.5d"); expression.getValue(); assertCanCompile(expression); assertEquals(2.5d,expression.getValue()); expression = parse("+T(Double).valueOf(2.5d)"); expression.getValue(); assertCanCompile(expression); assertEquals(2.5d,expression.getValue()); expression = parse("T(Integer).valueOf(2)+6"); assertEquals(8,expression.getValue()); assertCanCompile(expression); assertEquals(8,expression.getValue()); expression = parse("T(Integer).valueOf(1)+T(Integer).valueOf(3)"); assertEquals(4,expression.getValue()); assertCanCompile(expression); assertEquals(4,expression.getValue()); expression = parse("1+T(Integer).valueOf(3)"); assertEquals(4,expression.getValue()); assertCanCompile(expression); assertEquals(4,expression.getValue()); expression = parse("T(Float).valueOf(2.0f)+6"); assertEquals(8.0f,expression.getValue()); assertCantCompile(expression); expression = parse("T(Float).valueOf(2.0f)+T(Float).valueOf(3.0f)"); assertEquals(5.0f,expression.getValue()); assertCanCompile(expression); assertEquals(5.0f,expression.getValue()); expression = parse("3L+T(Long).valueOf(4L)"); assertEquals(7L,expression.getValue()); assertCanCompile(expression); assertEquals(7L,expression.getValue()); expression = parse("T(Long).valueOf(2L)+6"); assertEquals(8L,expression.getValue()); assertCantCompile(expression); expression = parse("T(Long).valueOf(2L)+T(Long).valueOf(3L)"); assertEquals(5L,expression.getValue()); assertCanCompile(expression); assertEquals(5L,expression.getValue()); expression = parse("1L+T(Long).valueOf(2L)"); assertEquals(3L,expression.getValue()); assertCanCompile(expression); assertEquals(3L,expression.getValue()); } @Test public void opMinus() throws Exception { expression = parse("2-2"); expression.getValue(); assertCanCompile(expression); assertEquals(0,expression.getValue()); expression = parse("4L-2L"); expression.getValue(); assertCanCompile(expression); assertEquals(2L,expression.getValue()); expression = parse("4.0f-2.0f"); expression.getValue(); assertCanCompile(expression); assertEquals(2.0f,expression.getValue()); expression = parse("3.0d-4.0d"); expression.getValue(); assertCanCompile(expression); assertEquals(-1.0d,expression.getValue()); expression = parse("-1"); expression.getValue(); assertCanCompile(expression); assertEquals(-1,expression.getValue()); expression = parse("-1L"); expression.getValue(); assertCanCompile(expression); assertEquals(-1L,expression.getValue()); expression = parse("-1.5f"); expression.getValue(); assertCanCompile(expression); assertEquals(-1.5f,expression.getValue()); expression = parse("-2.5d"); expression.getValue(); assertCanCompile(expression); assertEquals(-2.5d,expression.getValue()); expression = parse("T(Integer).valueOf(2)-6"); assertEquals(-4,expression.getValue()); assertCanCompile(expression); assertEquals(-4,expression.getValue()); expression = parse("T(Integer).valueOf(1)-T(Integer).valueOf(3)"); assertEquals(-2,expression.getValue()); assertCanCompile(expression); assertEquals(-2,expression.getValue()); expression = parse("4-T(Integer).valueOf(3)"); assertEquals(1,expression.getValue()); assertCanCompile(expression); assertEquals(1,expression.getValue()); expression = parse("T(Float).valueOf(2.0f)-6"); assertEquals(-4.0f,expression.getValue()); assertCantCompile(expression); expression = parse("T(Float).valueOf(8.0f)-T(Float).valueOf(3.0f)"); assertEquals(5.0f,expression.getValue()); assertCanCompile(expression); assertEquals(5.0f,expression.getValue()); expression = parse("11L-T(Long).valueOf(4L)"); assertEquals(7L,expression.getValue()); assertCanCompile(expression); assertEquals(7L,expression.getValue()); expression = parse("T(Long).valueOf(9L)-6"); assertEquals(3L,expression.getValue()); assertCantCompile(expression); expression = parse("T(Long).valueOf(4L)-T(Long).valueOf(3L)"); assertEquals(1L,expression.getValue()); assertCanCompile(expression); assertEquals(1L,expression.getValue()); expression = parse("8L-T(Long).valueOf(2L)"); assertEquals(6L,expression.getValue()); assertCanCompile(expression); assertEquals(6L,expression.getValue()); } @Test public void opMultiply() throws Exception { expression = parse("2*2"); expression.getValue(); assertCanCompile(expression); assertEquals(4,expression.getValue()); expression = parse("2L*2L"); expression.getValue(); assertCanCompile(expression); assertEquals(4L,expression.getValue()); expression = parse("2.0f*2.0f"); expression.getValue(); assertCanCompile(expression); assertEquals(4.0f,expression.getValue()); expression = parse("3.0d*4.0d"); expression.getValue(); assertCanCompile(expression); assertEquals(12.0d,expression.getValue()); expression = parse("T(Float).valueOf(2.0f)*6"); assertEquals(12.0f,expression.getValue()); assertCantCompile(expression); expression = parse("T(Float).valueOf(8.0f)*T(Float).valueOf(3.0f)"); assertEquals(24.0f,expression.getValue()); assertCanCompile(expression); assertEquals(24.0f,expression.getValue()); expression = parse("11L*T(Long).valueOf(4L)"); assertEquals(44L,expression.getValue()); assertCanCompile(expression); assertEquals(44L,expression.getValue()); expression = parse("T(Long).valueOf(9L)*6"); assertEquals(54L,expression.getValue()); assertCantCompile(expression); expression = parse("T(Long).valueOf(4L)*T(Long).valueOf(3L)"); assertEquals(12L,expression.getValue()); assertCanCompile(expression); assertEquals(12L,expression.getValue()); expression = parse("8L*T(Long).valueOf(2L)"); assertEquals(16L,expression.getValue()); assertCanCompile(expression); assertEquals(16L,expression.getValue()); expression = parse("T(Float).valueOf(8.0f)*-T(Float).valueOf(3.0f)"); assertEquals(-24.0f,expression.getValue()); assertCanCompile(expression); assertEquals(-24.0f,expression.getValue()); } @Test public void opDivide() throws Exception { expression = parse("2/2"); expression.getValue(); assertCanCompile(expression); assertEquals(1,expression.getValue()); expression = parse("2L/2L"); expression.getValue(); assertCanCompile(expression); assertEquals(1L,expression.getValue()); expression = parse("2.0f/2.0f"); expression.getValue(); assertCanCompile(expression); assertEquals(1.0f,expression.getValue()); expression = parse("3.0d/4.0d"); expression.getValue(); assertCanCompile(expression); assertEquals(0.75d,expression.getValue()); expression = parse("T(Float).valueOf(6.0f)/2"); assertEquals(3.0f,expression.getValue()); assertCantCompile(expression); expression = parse("T(Float).valueOf(8.0f)/T(Float).valueOf(2.0f)"); assertEquals(4.0f,expression.getValue()); assertCanCompile(expression); assertEquals(4.0f,expression.getValue()); expression = parse("12L/T(Long).valueOf(4L)"); assertEquals(3L,expression.getValue()); assertCanCompile(expression); assertEquals(3L,expression.getValue()); expression = parse("T(Long).valueOf(44L)/11"); assertEquals(4L,expression.getValue()); assertCantCompile(expression); expression = parse("T(Long).valueOf(4L)/T(Long).valueOf(2L)"); assertEquals(2L,expression.getValue()); assertCanCompile(expression); assertEquals(2L,expression.getValue()); expression = parse("8L/T(Long).valueOf(2L)"); assertEquals(4L,expression.getValue()); assertCanCompile(expression); assertEquals(4L,expression.getValue()); expression = parse("T(Float).valueOf(8.0f)/-T(Float).valueOf(4.0f)"); assertEquals(-2.0f,expression.getValue()); assertCanCompile(expression); assertEquals(-2.0f,expression.getValue()); } @Test public void opModulus_12041() throws Exception { expression = parse("2%2"); assertEquals(0,expression.getValue()); assertCanCompile(expression); assertEquals(0,expression.getValue()); expression = parse("payload%2==0"); assertTrue(expression.getValue(new GenericMessageTestHelper(4),Boolean.TYPE)); assertFalse(expression.getValue(new GenericMessageTestHelper(5),Boolean.TYPE)); assertCanCompile(expression); assertTrue(expression.getValue(new GenericMessageTestHelper(4),Boolean.TYPE)); assertFalse(expression.getValue(new GenericMessageTestHelper(5),Boolean.TYPE)); expression = parse("8%3"); assertEquals(2,expression.getValue()); assertCanCompile(expression); assertEquals(2,expression.getValue()); expression = parse("17L%5L"); assertEquals(2L,expression.getValue()); assertCanCompile(expression); assertEquals(2L,expression.getValue()); expression = parse("3.0f%2.0f"); assertEquals(1.0f,expression.getValue()); assertCanCompile(expression); assertEquals(1.0f,expression.getValue()); expression = parse("3.0d%4.0d"); assertEquals(3.0d,expression.getValue()); assertCanCompile(expression); assertEquals(3.0d,expression.getValue()); expression = parse("T(Float).valueOf(6.0f)%2"); assertEquals(0.0f,expression.getValue()); assertCantCompile(expression); expression = parse("T(Float).valueOf(6.0f)%4"); assertEquals(2.0f,expression.getValue()); assertCantCompile(expression); expression = parse("T(Float).valueOf(8.0f)%T(Float).valueOf(3.0f)"); assertEquals(2.0f,expression.getValue()); assertCanCompile(expression); assertEquals(2.0f,expression.getValue()); expression = parse("13L%T(Long).valueOf(4L)"); assertEquals(1L,expression.getValue()); assertCanCompile(expression); assertEquals(1L,expression.getValue()); expression = parse("T(Long).valueOf(44L)%12"); assertEquals(8L,expression.getValue()); assertCantCompile(expression); expression = parse("T(Long).valueOf(9L)%T(Long).valueOf(2L)"); assertEquals(1L,expression.getValue()); assertCanCompile(expression); assertEquals(1L,expression.getValue()); expression = parse("7L%T(Long).valueOf(2L)"); assertEquals(1L,expression.getValue()); assertCanCompile(expression); assertEquals(1L,expression.getValue()); expression = parse("T(Float).valueOf(9.0f)%-T(Float).valueOf(4.0f)"); assertEquals(1.0f,expression.getValue()); assertCanCompile(expression); assertEquals(1.0f,expression.getValue()); } @Test public void constructorReference() throws Exception { // simple ctor expression = parser.parseExpression("new String('123')"); assertEquals("123",expression.getValue()); assertCanCompile(expression); assertEquals("123",expression.getValue()); String testclass8 = "org.springframework.expression.spel.SpelCompilationCoverageTests$TestClass8"; // multi arg ctor that includes primitives expression = parser.parseExpression("new "+testclass8+"(42,'123',4.0d,true)"); assertEquals(testclass8,expression.getValue().getClass().getName()); assertCanCompile(expression); Object o = expression.getValue(); assertEquals(testclass8,o.getClass().getName()); TestClass8 tc8 = (TestClass8)o; assertEquals(42,tc8.i); assertEquals("123",tc8.s); assertEquals(4.0d,tc8.d,0.5d); assertEquals(true,tc8.z); // no-arg ctor expression = parser.parseExpression("new "+testclass8+"()"); assertEquals(testclass8,expression.getValue().getClass().getName()); assertCanCompile(expression); o = expression.getValue(); assertEquals(testclass8,o.getClass().getName()); // pass primitive to reference type ctor expression = parser.parseExpression("new "+testclass8+"(42)"); assertEquals(testclass8,expression.getValue().getClass().getName()); assertCanCompile(expression); o = expression.getValue(); assertEquals(testclass8,o.getClass().getName()); tc8 = (TestClass8)o; assertEquals(42,tc8.i); // private class, can't compile it String testclass9 = "org.springframework.expression.spel.SpelCompilationCoverageTests$TestClass9"; expression = parser.parseExpression("new "+testclass9+"(42)"); assertEquals(testclass9,expression.getValue().getClass().getName()); assertCantCompile(expression); } @Test public void methodReference() throws Exception { TestClass5 tc = new TestClass5(); // non-static method, no args, void return expression = parser.parseExpression("one()"); assertCantCompile(expression); expression.getValue(tc); assertCanCompile(expression); tc.reset(); expression.getValue(tc); assertEquals(1,tc.i); tc.reset(); // static method, no args, void return expression = parser.parseExpression("two()"); assertCantCompile(expression); expression.getValue(tc); assertCanCompile(expression); tc.reset(); expression.getValue(tc); assertEquals(1,TestClass5._i); tc.reset(); // non-static method, reference type return expression = parser.parseExpression("three()"); assertCantCompile(expression); expression.getValue(tc); assertCanCompile(expression); tc.reset(); assertEquals("hello",expression.getValue(tc)); tc.reset(); // non-static method, primitive type return expression = parser.parseExpression("four()"); assertCantCompile(expression); expression.getValue(tc); assertCanCompile(expression); tc.reset(); assertEquals(3277700L,expression.getValue(tc)); tc.reset(); // static method, reference type return expression = parser.parseExpression("five()"); assertCantCompile(expression); expression.getValue(tc); assertCanCompile(expression); tc.reset(); assertEquals("hello",expression.getValue(tc)); tc.reset(); // static method, primitive type return expression = parser.parseExpression("six()"); assertCantCompile(expression); expression.getValue(tc); assertCanCompile(expression); tc.reset(); assertEquals(3277700L,expression.getValue(tc)); tc.reset(); // non-static method, one parameter of reference type expression = parser.parseExpression("seven(\"foo\")"); assertCantCompile(expression); expression.getValue(tc); assertCanCompile(expression); tc.reset(); expression.getValue(tc); assertEquals("foo",tc.s); tc.reset(); // static method, one parameter of reference type expression = parser.parseExpression("eight(\"bar\")"); assertCantCompile(expression); expression.getValue(tc); assertCanCompile(expression); tc.reset(); expression.getValue(tc); assertEquals("bar",TestClass5._s); tc.reset(); // non-static method, one parameter of primitive type expression = parser.parseExpression("nine(231)"); assertCantCompile(expression); expression.getValue(tc); assertCanCompile(expression); tc.reset(); expression.getValue(tc); assertEquals(231,tc.i); tc.reset(); // static method, one parameter of primitive type expression = parser.parseExpression("ten(111)"); assertCantCompile(expression); expression.getValue(tc); assertCanCompile(expression); tc.reset(); expression.getValue(tc); assertEquals(111,TestClass5._i); tc.reset(); // non-static method, varargs with reference type expression = parser.parseExpression("eleven(\"a\",\"b\",\"c\")"); assertCantCompile(expression); expression.getValue(tc); assertCantCompile(expression); // Varargs is not yet supported expression = parser.parseExpression("eleven()"); assertCantCompile(expression); expression.getValue(tc); assertCantCompile(expression); // Varargs is not yet supported // static method, varargs with primitive type expression = parser.parseExpression("twelve(1,2,3)"); assertCantCompile(expression); expression.getValue(tc); assertCantCompile(expression); // Varargs is not yet supported expression = parser.parseExpression("twelve()"); assertCantCompile(expression); expression.getValue(tc); assertCantCompile(expression); // Varargs is not yet supported // method that gets type converted parameters // Converting from an int to a string expression = parser.parseExpression("seven(123)"); assertCantCompile(expression); expression.getValue(tc); assertEquals("123",tc.s); assertCantCompile(expression); // Uncompilable as argument conversion is occurring Expression expression = parser.parseExpression("'abcd'.substring(index1,index2)"); String resultI = expression.getValue(new TestClass1(),String.class); assertCanCompile(expression); String resultC = expression.getValue(new TestClass1(),String.class); assertEquals("bc",resultI); assertEquals("bc",resultC); // Converting from an int to a Number expression = parser.parseExpression("takeNumber(123)"); assertCantCompile(expression); expression.getValue(tc); assertEquals("123",tc.s); tc.reset(); assertCanCompile(expression); // The generated code should include boxing of the int to a Number expression.getValue(tc); assertEquals("123",tc.s); // Passing a subtype expression = parser.parseExpression("takeNumber(T(Integer).valueOf(42))"); assertCantCompile(expression); expression.getValue(tc); assertEquals("42",tc.s); tc.reset(); assertCanCompile(expression); // The generated code should include boxing of the int to a Number expression.getValue(tc); assertEquals("42",tc.s); // Passing a subtype expression = parser.parseExpression("takeString(T(Integer).valueOf(42))"); assertCantCompile(expression); expression.getValue(tc); assertEquals("42",tc.s); tc.reset(); assertCantCompile(expression); // method takes a string and we are passing an Integer } @Test public void errorHandling() throws Exception { TestClass5 tc = new TestClass5(); // changing target // from primitive array to reference type array int[] is = new int[]{1,2,3}; String[] strings = new String[]{"a","b","c"}; expression = parser.parseExpression("[1]"); assertEquals(2,expression.getValue(is)); assertCanCompile(expression); assertEquals(2,expression.getValue(is)); try { assertEquals(2,expression.getValue(strings)); fail(); } catch (SpelEvaluationException see) { assertTrue(see.getCause() instanceof ClassCastException); } SpelCompiler.revertToInterpreted(expression); assertEquals("b",expression.getValue(strings)); assertCanCompile(expression); assertEquals("b",expression.getValue(strings)); tc.field = "foo"; expression = parser.parseExpression("seven(field)"); assertCantCompile(expression); expression.getValue(tc); assertEquals("foo",tc.s); assertCanCompile(expression); tc.reset(); tc.field="bar"; expression.getValue(tc); // method with changing parameter types (change reference type) tc.obj = "foo"; expression = parser.parseExpression("seven(obj)"); assertCantCompile(expression); expression.getValue(tc); assertEquals("foo",tc.s); assertCanCompile(expression); tc.reset(); tc.obj=new Integer(42); try { expression.getValue(tc); fail(); } catch (SpelEvaluationException see) { assertTrue(see.getCause() instanceof ClassCastException); } // method with changing target expression = parser.parseExpression("#root.charAt(0)"); assertEquals('a',expression.getValue("abc")); assertCanCompile(expression); try { expression.getValue(new Integer(42)); fail(); } catch (SpelEvaluationException see) { // java.lang.Integer cannot be cast to java.lang.String assertTrue(see.getCause() instanceof ClassCastException); } } @Test public void methodReference_staticMethod() throws Exception { Expression expression = parser.parseExpression("T(Integer).valueOf(42)"); int resultI = expression.getValue(new TestClass1(),Integer.TYPE); assertCanCompile(expression); int resultC = expression.getValue(new TestClass1(),Integer.TYPE); assertEquals(42,resultI); assertEquals(42,resultC); } @Test public void methodReference_literalArguments_int() throws Exception { Expression expression = parser.parseExpression("'abcd'.substring(1,3)"); String resultI = expression.getValue(new TestClass1(),String.class); assertCanCompile(expression); String resultC = expression.getValue(new TestClass1(),String.class); assertEquals("bc",resultI); assertEquals("bc",resultC); } @Test public void methodReference_simpleInstanceMethodNoArg() throws Exception { Expression expression = parser.parseExpression("toString()"); String resultI = expression.getValue(42,String.class); assertCanCompile(expression); String resultC = expression.getValue(42,String.class); assertEquals("42",resultI); assertEquals("42",resultC); } @Test public void methodReference_simpleInstanceMethodNoArgReturnPrimitive() throws Exception { expression = parser.parseExpression("intValue()"); int resultI = expression.getValue(new Integer(42),Integer.TYPE); assertEquals(42,resultI); assertCanCompile(expression); int resultC = expression.getValue(new Integer(42),Integer.TYPE); assertEquals(42,resultC); } @Test public void methodReference_simpleInstanceMethodOneArgReturnPrimitive1() throws Exception { Expression expression = parser.parseExpression("indexOf('b')"); int resultI = expression.getValue("abc",Integer.TYPE); assertCanCompile(expression); int resultC = expression.getValue("abc",Integer.TYPE); assertEquals(1,resultI); assertEquals(1,resultC); } @Test public void methodReference_simpleInstanceMethodOneArgReturnPrimitive2() throws Exception { expression = parser.parseExpression("charAt(2)"); char resultI = expression.getValue("abc",Character.TYPE); assertEquals('c',resultI); assertCanCompile(expression); char resultC = expression.getValue("abc",Character.TYPE); assertEquals('c',resultC); } @Test public void compoundExpression() throws Exception { Payload payload = new Payload(); expression = parser.parseExpression("DR[0]"); assertEquals("instanceof Two",expression.getValue(payload).toString()); assertCanCompile(expression); assertEquals("instanceof Two",expression.getValue(payload).toString()); ast = getAst(); assertEquals("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Two",ast.getExitDescriptor()); expression = parser.parseExpression("holder.three"); assertEquals("org.springframework.expression.spel.SpelCompilationCoverageTests$Three",expression.getValue(payload).getClass().getName()); assertCanCompile(expression); assertEquals("org.springframework.expression.spel.SpelCompilationCoverageTests$Three",expression.getValue(payload).getClass().getName()); ast = getAst(); assertEquals("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three",ast.getExitDescriptor()); expression = parser.parseExpression("DR[0]"); assertEquals("org.springframework.expression.spel.SpelCompilationCoverageTests$Two",expression.getValue(payload).getClass().getName()); assertCanCompile(expression); assertEquals("org.springframework.expression.spel.SpelCompilationCoverageTests$Two",expression.getValue(payload).getClass().getName()); assertEquals("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Two",getAst().getExitDescriptor()); expression = parser.parseExpression("DR[0].three"); assertEquals("org.springframework.expression.spel.SpelCompilationCoverageTests$Three",expression.getValue(payload).getClass().getName()); assertCanCompile(expression); assertEquals("org.springframework.expression.spel.SpelCompilationCoverageTests$Three",expression.getValue(payload).getClass().getName()); ast = getAst(); assertEquals("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three",ast.getExitDescriptor()); expression = parser.parseExpression("DR[0].three.four"); assertEquals(0.04d,expression.getValue(payload)); assertCanCompile(expression); assertEquals(0.04d,expression.getValue(payload)); assertEquals("D",getAst().getExitDescriptor()); } @Test public void mixingItUp_indexerOpEqTernary() throws Exception { Map m = new HashMap(); m.put("andy","778"); expression = parse("['andy']==null?1:2"); System.out.println(expression.getValue(m)); assertCanCompile(expression); assertEquals(2,expression.getValue(m)); m.remove("andy"); assertEquals(1,expression.getValue(m)); } @Test public void propertyReference() throws Exception { TestClass6 tc = new TestClass6(); // non static field expression = parser.parseExpression("orange"); assertCantCompile(expression); assertEquals("value1",expression.getValue(tc)); assertCanCompile(expression); assertEquals("value1",expression.getValue(tc)); // static field expression = parser.parseExpression("apple"); assertCantCompile(expression); assertEquals("value2",expression.getValue(tc)); assertCanCompile(expression); assertEquals("value2",expression.getValue(tc)); // non static getter expression = parser.parseExpression("banana"); assertCantCompile(expression); assertEquals("value3",expression.getValue(tc)); assertCanCompile(expression); assertEquals("value3",expression.getValue(tc)); // static getter expression = parser.parseExpression("plum"); assertCantCompile(expression); assertEquals("value4",expression.getValue(tc)); assertCanCompile(expression); assertEquals("value4",expression.getValue(tc)); } @SuppressWarnings("unchecked") @Test public void indexer() throws Exception { String[] sss = new String[]{"a","b","c"}; Number[] ns = new Number[]{2,8,9}; int[] is = new int[]{8,9,10}; double[] ds = new double[]{3.0d,4.0d,5.0d}; long[] ls = new long[]{2L,3L,4L}; short[] ss = new short[]{(short)33,(short)44,(short)55}; float[] fs = new float[]{6.0f,7.0f,8.0f}; byte[] bs = new byte[]{(byte)2,(byte)3,(byte)4}; char[] cs = new char[]{'a','b','c'}; // Access String (reference type) array expression = parser.parseExpression("[0]"); assertEquals("a",expression.getValue(sss)); assertCanCompile(expression); assertEquals("a",expression.getValue(sss)); assertEquals("Ljava/lang/String",getAst().getExitDescriptor()); expression = parser.parseExpression("[1]"); assertEquals(8,expression.getValue(ns)); assertCanCompile(expression); assertEquals(8,expression.getValue(ns)); assertEquals("Ljava/lang/Number",getAst().getExitDescriptor()); // Access int array expression = parser.parseExpression("[2]"); assertEquals(10,expression.getValue(is)); assertCanCompile(expression); assertEquals(10,expression.getValue(is)); assertEquals("I",getAst().getExitDescriptor()); // Access double array expression = parser.parseExpression("[1]"); assertEquals(4.0d,expression.getValue(ds)); assertCanCompile(expression); assertEquals(4.0d,expression.getValue(ds)); assertEquals("D",getAst().getExitDescriptor()); // Access long array expression = parser.parseExpression("[0]"); assertEquals(2L,expression.getValue(ls)); assertCanCompile(expression); assertEquals(2L,expression.getValue(ls)); assertEquals("J",getAst().getExitDescriptor()); // Access short array expression = parser.parseExpression("[2]"); assertEquals((short)55,expression.getValue(ss)); assertCanCompile(expression); assertEquals((short)55,expression.getValue(ss)); assertEquals("S",getAst().getExitDescriptor()); // Access float array expression = parser.parseExpression("[0]"); assertEquals(6.0f,expression.getValue(fs)); assertCanCompile(expression); assertEquals(6.0f,expression.getValue(fs)); assertEquals("F",getAst().getExitDescriptor()); // Access byte array expression = parser.parseExpression("[2]"); assertEquals((byte)4,expression.getValue(bs)); assertCanCompile(expression); assertEquals((byte)4,expression.getValue(bs)); assertEquals("B",getAst().getExitDescriptor()); // Access char array expression = parser.parseExpression("[1]"); assertEquals('b',expression.getValue(cs)); assertCanCompile(expression); assertEquals('b',expression.getValue(cs)); assertEquals("C",getAst().getExitDescriptor()); // Collections List strings = new ArrayList(); strings.add("aaa"); strings.add("bbb"); strings.add("ccc"); expression = parser.parseExpression("[1]"); assertEquals("bbb",expression.getValue(strings)); assertCanCompile(expression); assertEquals("bbb",expression.getValue(strings)); assertEquals("Ljava/lang/String",getAst().getExitDescriptor()); List ints = new ArrayList(); ints.add(123); ints.add(456); ints.add(789); expression = parser.parseExpression("[2]"); assertEquals(789,expression.getValue(ints)); assertCanCompile(expression); assertEquals(789,expression.getValue(ints)); assertEquals("Ljava/lang/Integer",getAst().getExitDescriptor()); // Maps Map map1 = new HashMap(); map1.put("aaa", 111); map1.put("bbb", 222); map1.put("ccc", 333); expression = parser.parseExpression("['aaa']"); assertEquals(111,expression.getValue(map1)); assertCanCompile(expression); assertEquals(111,expression.getValue(map1)); assertEquals("Ljava/lang/Integer",getAst().getExitDescriptor()); // Object TestClass6 tc = new TestClass6(); expression = parser.parseExpression("['orange']"); assertEquals("value1",expression.getValue(tc)); assertCanCompile(expression); assertEquals("value1",expression.getValue(tc)); assertEquals("Ljava/lang/String",getAst().getExitDescriptor()); expression = parser.parseExpression("['peach']"); assertEquals(34L,expression.getValue(tc)); assertCanCompile(expression); assertEquals(34L,expression.getValue(tc)); assertEquals("J",getAst().getExitDescriptor()); // getter expression = parser.parseExpression("['banana']"); assertEquals("value3",expression.getValue(tc)); assertCanCompile(expression); assertEquals("value3",expression.getValue(tc)); assertEquals("Ljava/lang/String",getAst().getExitDescriptor()); // list of arrays List listOfStringArrays = new ArrayList(); listOfStringArrays.add(new String[]{"a","b","c"}); listOfStringArrays.add(new String[]{"d","e","f"}); expression = parser.parseExpression("[1]"); assertEquals("d e f",stringify(expression.getValue(listOfStringArrays))); assertCanCompile(expression); assertEquals("d e f",stringify(expression.getValue(listOfStringArrays))); assertEquals("[Ljava/lang/String",getAst().getExitDescriptor()); List listOfIntegerArrays = new ArrayList(); listOfIntegerArrays.add(new Integer[]{1,2,3}); listOfIntegerArrays.add(new Integer[]{4,5,6}); expression = parser.parseExpression("[0]"); assertEquals("1 2 3",stringify(expression.getValue(listOfIntegerArrays))); assertCanCompile(expression); assertEquals("1 2 3",stringify(expression.getValue(listOfIntegerArrays))); assertEquals("[Ljava/lang/Integer",getAst().getExitDescriptor()); expression = parser.parseExpression("[0][1]"); assertEquals(2,expression.getValue(listOfIntegerArrays)); assertCanCompile(expression); assertEquals(2,expression.getValue(listOfIntegerArrays)); assertEquals("Ljava/lang/Integer",getAst().getExitDescriptor()); // array of lists List[] stringArrayOfLists = new ArrayList[2]; stringArrayOfLists[0] = new ArrayList(); stringArrayOfLists[0].add("a"); stringArrayOfLists[0].add("b"); stringArrayOfLists[0].add("c"); stringArrayOfLists[1] = new ArrayList(); stringArrayOfLists[1].add("d"); stringArrayOfLists[1].add("e"); stringArrayOfLists[1].add("f"); expression = parser.parseExpression("[1]"); assertEquals("d e f",stringify(expression.getValue(stringArrayOfLists))); assertCanCompile(expression); assertEquals("d e f",stringify(expression.getValue(stringArrayOfLists))); assertEquals("Ljava/util/ArrayList",getAst().getExitDescriptor()); expression = parser.parseExpression("[1][2]"); assertEquals("f",stringify(expression.getValue(stringArrayOfLists))); assertCanCompile(expression); assertEquals("f",stringify(expression.getValue(stringArrayOfLists))); assertEquals("Ljava/lang/String",getAst().getExitDescriptor()); // array of arrays String[][] referenceTypeArrayOfArrays = new String[][]{new String[]{"a","b","c"},new String[]{"d","e","f"}}; expression = parser.parseExpression("[1]"); assertEquals("d e f",stringify(expression.getValue(referenceTypeArrayOfArrays))); assertCanCompile(expression); assertEquals("[Ljava/lang/String",getAst().getExitDescriptor()); assertEquals("d e f",stringify(expression.getValue(referenceTypeArrayOfArrays))); assertEquals("[Ljava/lang/String",getAst().getExitDescriptor()); expression = parser.parseExpression("[1][2]"); assertEquals("f",stringify(expression.getValue(referenceTypeArrayOfArrays))); assertCanCompile(expression); assertEquals("f",stringify(expression.getValue(referenceTypeArrayOfArrays))); assertEquals("Ljava/lang/String",getAst().getExitDescriptor()); int[][] primitiveTypeArrayOfArrays = new int[][]{new int[]{1,2,3},new int[]{4,5,6}}; expression = parser.parseExpression("[1]"); assertEquals("4 5 6",stringify(expression.getValue(primitiveTypeArrayOfArrays))); assertCanCompile(expression); assertEquals("4 5 6",stringify(expression.getValue(primitiveTypeArrayOfArrays))); assertEquals("[I",getAst().getExitDescriptor()); expression = parser.parseExpression("[1][2]"); assertEquals("6",stringify(expression.getValue(primitiveTypeArrayOfArrays))); assertCanCompile(expression); assertEquals("6",stringify(expression.getValue(primitiveTypeArrayOfArrays))); assertEquals("I",getAst().getExitDescriptor()); // list of lists of reference types List> listOfListOfStrings = new ArrayList>(); List list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); listOfListOfStrings.add(list); list = new ArrayList(); list.add("d"); list.add("e"); list.add("f"); listOfListOfStrings.add(list); expression = parser.parseExpression("[1]"); assertEquals("d e f",stringify(expression.getValue(listOfListOfStrings))); assertCanCompile(expression); assertEquals("Ljava/util/ArrayList",getAst().getExitDescriptor()); assertEquals("d e f",stringify(expression.getValue(listOfListOfStrings))); assertEquals("Ljava/util/ArrayList",getAst().getExitDescriptor()); expression = parser.parseExpression("[1][2]"); assertEquals("f",stringify(expression.getValue(listOfListOfStrings))); assertCanCompile(expression); assertEquals("f",stringify(expression.getValue(listOfListOfStrings))); assertEquals("Ljava/lang/String",getAst().getExitDescriptor()); // Map of lists Map> mapToLists = new HashMap>(); list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); mapToLists.put("foo", list); expression = parser.parseExpression("['foo']"); assertEquals("a b c",stringify(expression.getValue(mapToLists))); assertCanCompile(expression); assertEquals("Ljava/util/ArrayList",getAst().getExitDescriptor()); assertEquals("a b c",stringify(expression.getValue(mapToLists))); assertEquals("Ljava/util/ArrayList",getAst().getExitDescriptor()); expression = parser.parseExpression("['foo'][2]"); assertEquals("c",stringify(expression.getValue(mapToLists))); assertCanCompile(expression); assertEquals("c",stringify(expression.getValue(mapToLists))); assertEquals("Ljava/lang/String",getAst().getExitDescriptor()); // Map to array Map mapToIntArray = new HashMap(); StandardEvaluationContext ctx = new StandardEvaluationContext(); ctx.addPropertyAccessor(new CompilableMapAccessor()); mapToIntArray.put("foo",new int[]{1,2,3}); expression = parser.parseExpression("['foo']"); assertEquals("1 2 3",stringify(expression.getValue(mapToIntArray))); assertCanCompile(expression); assertEquals("[I",getAst().getExitDescriptor()); assertEquals("1 2 3",stringify(expression.getValue(mapToIntArray))); assertEquals("[I",getAst().getExitDescriptor()); expression = parser.parseExpression("['foo'][1]"); assertEquals(2,expression.getValue(mapToIntArray)); assertCanCompile(expression); assertEquals(2,expression.getValue(mapToIntArray)); expression = parser.parseExpression("foo"); assertEquals("1 2 3",stringify(expression.getValue(ctx,mapToIntArray))); assertCanCompile(expression); assertEquals("1 2 3",stringify(expression.getValue(ctx,mapToIntArray))); assertEquals("Ljava/lang/Object",getAst().getExitDescriptor()); expression = parser.parseExpression("foo[1]"); assertEquals(2,expression.getValue(ctx,mapToIntArray)); assertCanCompile(expression); assertEquals(2,expression.getValue(ctx,mapToIntArray)); expression = parser.parseExpression("['foo'][2]"); assertEquals("3",stringify(expression.getValue(ctx,mapToIntArray))); assertCanCompile(expression); assertEquals("3",stringify(expression.getValue(ctx,mapToIntArray))); assertEquals("I",getAst().getExitDescriptor()); // Map array Map[] mapArray = new Map[1]; mapArray[0] = new HashMap(); mapArray[0].put("key", "value1"); expression = parser.parseExpression("[0]"); assertEquals("{key=value1}",stringify(expression.getValue(mapArray))); assertCanCompile(expression); assertEquals("Ljava/util/Map",getAst().getExitDescriptor()); assertEquals("{key=value1}",stringify(expression.getValue(mapArray))); assertEquals("Ljava/util/Map",getAst().getExitDescriptor()); expression = parser.parseExpression("[0]['key']"); assertEquals("value1",stringify(expression.getValue(mapArray))); assertCanCompile(expression); assertEquals("value1",stringify(expression.getValue(mapArray))); assertEquals("Ljava/lang/String",getAst().getExitDescriptor()); } @Test public void mixingItUp_propertyAccessIndexerOpLtTernaryRootNull() throws Exception { Payload payload = new Payload(); expression = parser.parseExpression("DR[0].three"); Object v = expression.getValue(payload); assertEquals("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three",getAst().getExitDescriptor()); Expression expression = parser.parseExpression("DR[0].three.four lt 0.1d?#root:null"); v = expression.getValue(payload); SpelExpression sExpr = (SpelExpression)expression; Ternary ternary = (Ternary)sExpr.getAST(); OpLT oplt = (OpLT)ternary.getChild(0); CompoundExpression cExpr = (CompoundExpression)oplt.getLeftOperand(); String cExprExitDescriptor = cExpr.getExitDescriptor(); assertEquals("D",cExprExitDescriptor); assertEquals("Z",oplt.getExitDescriptor()); assertCanCompile(expression); Object vc = expression.getValue(payload); assertEquals(payload,v); assertEquals(payload,vc); payload.DR[0].three.four = 0.13d; vc = expression.getValue(payload); assertNull(vc); } @Test public void variantGetter() throws Exception { Payload2Holder holder = new Payload2Holder(); StandardEvaluationContext ctx = new StandardEvaluationContext(); ctx.addPropertyAccessor(new MyAccessor()); expression = parser.parseExpression("payload2.var1"); Object v = expression.getValue(ctx,holder); assertEquals("abc",v); // // time it interpreted // long stime = System.currentTimeMillis(); // for (int i=0;i<100000;i++) { // v = expression.getValue(ctx,holder); // } // System.out.println((System.currentTimeMillis()-stime)); // assertCanCompile(expression); v = expression.getValue(ctx,holder); assertEquals("abc",v); // // // time it compiled // stime = System.currentTimeMillis(); // for (int i=0;i<100000;i++) { // v = expression.getValue(ctx,holder); // } // System.out.println((System.currentTimeMillis()-stime)); } @Test public void compilerWithGenerics_12040() { expression = parser.parseExpression("payload!=2"); assertTrue(expression.getValue(new GenericMessageTestHelper(4),Boolean.class)); assertCanCompile(expression); assertFalse(expression.getValue(new GenericMessageTestHelper(2),Boolean.class)); expression = parser.parseExpression("2!=payload"); assertTrue(expression.getValue(new GenericMessageTestHelper(4),Boolean.class)); assertCanCompile(expression); assertFalse(expression.getValue(new GenericMessageTestHelper(2),Boolean.class)); expression = parser.parseExpression("payload!=6L"); assertTrue(expression.getValue(new GenericMessageTestHelper(4L),Boolean.class)); assertCanCompile(expression); assertFalse(expression.getValue(new GenericMessageTestHelper(6L),Boolean.class)); expression = parser.parseExpression("payload==2"); assertFalse(expression.getValue(new GenericMessageTestHelper(4),Boolean.class)); assertCanCompile(expression); assertTrue(expression.getValue(new GenericMessageTestHelper(2),Boolean.class)); expression = parser.parseExpression("2==payload"); assertFalse(expression.getValue(new GenericMessageTestHelper(4),Boolean.class)); assertCanCompile(expression); assertTrue(expression.getValue(new GenericMessageTestHelper(2),Boolean.class)); expression = parser.parseExpression("payload==6L"); assertFalse(expression.getValue(new GenericMessageTestHelper(4L),Boolean.class)); assertCanCompile(expression); assertTrue(expression.getValue(new GenericMessageTestHelper(6L),Boolean.class)); expression = parser.parseExpression("2==payload"); assertFalse(expression.getValue(new GenericMessageTestHelper(4),Boolean.class)); assertCanCompile(expression); assertTrue(expression.getValue(new GenericMessageTestHelper(2),Boolean.class)); expression = parser.parseExpression("payload/2"); assertEquals(2,expression.getValue(new GenericMessageTestHelper(4))); assertCanCompile(expression); assertEquals(3,expression.getValue(new GenericMessageTestHelper(6))); expression = parser.parseExpression("100/payload"); assertEquals(25,expression.getValue(new GenericMessageTestHelper(4))); assertCanCompile(expression); assertEquals(10,expression.getValue(new GenericMessageTestHelper(10))); expression = parser.parseExpression("payload+2"); assertEquals(6,expression.getValue(new GenericMessageTestHelper(4))); assertCanCompile(expression); assertEquals(8,expression.getValue(new GenericMessageTestHelper(6))); expression = parser.parseExpression("100+payload"); assertEquals(104,expression.getValue(new GenericMessageTestHelper(4))); assertCanCompile(expression); assertEquals(110,expression.getValue(new GenericMessageTestHelper(10))); expression = parser.parseExpression("payload-2"); assertEquals(2,expression.getValue(new GenericMessageTestHelper(4))); assertCanCompile(expression); assertEquals(4,expression.getValue(new GenericMessageTestHelper(6))); expression = parser.parseExpression("100-payload"); assertEquals(96,expression.getValue(new GenericMessageTestHelper(4))); assertCanCompile(expression); assertEquals(90,expression.getValue(new GenericMessageTestHelper(10))); expression = parser.parseExpression("payload*2"); assertEquals(8,expression.getValue(new GenericMessageTestHelper(4))); assertCanCompile(expression); assertEquals(12,expression.getValue(new GenericMessageTestHelper(6))); expression = parser.parseExpression("100*payload"); assertEquals(400,expression.getValue(new GenericMessageTestHelper(4))); assertCanCompile(expression); assertEquals(1000,expression.getValue(new GenericMessageTestHelper(10))); expression = parser.parseExpression("payload/2L"); assertEquals(2L,expression.getValue(new GenericMessageTestHelper(4L))); assertCanCompile(expression); assertEquals(3L,expression.getValue(new GenericMessageTestHelper(6L))); expression = parser.parseExpression("100L/payload"); assertEquals(25L,expression.getValue(new GenericMessageTestHelper(4L))); assertCanCompile(expression); assertEquals(10L,expression.getValue(new GenericMessageTestHelper(10L))); expression = parser.parseExpression("payload/2f"); assertEquals(2f,expression.getValue(new GenericMessageTestHelper(4f))); assertCanCompile(expression); assertEquals(3f,expression.getValue(new GenericMessageTestHelper(6f))); expression = parser.parseExpression("100f/payload"); assertEquals(25f,expression.getValue(new GenericMessageTestHelper(4f))); assertCanCompile(expression); assertEquals(10f,expression.getValue(new GenericMessageTestHelper(10f))); expression = parser.parseExpression("payload/2d"); assertEquals(2d,expression.getValue(new GenericMessageTestHelper(4d))); assertCanCompile(expression); assertEquals(3d,expression.getValue(new GenericMessageTestHelper(6d))); expression = parser.parseExpression("100d/payload"); assertEquals(25d,expression.getValue(new GenericMessageTestHelper(4d))); assertCanCompile(expression); assertEquals(10d,expression.getValue(new GenericMessageTestHelper(10d))); } // The new helper class here uses an upper bound on the generic @Test public void compilerWithGenerics_12040_2() { expression = parser.parseExpression("payload/2"); assertEquals(2,expression.getValue(new GenericMessageTestHelper2(4))); assertCanCompile(expression); assertEquals(3,expression.getValue(new GenericMessageTestHelper2(6))); expression = parser.parseExpression("9/payload"); assertEquals(1,expression.getValue(new GenericMessageTestHelper2(9))); assertCanCompile(expression); assertEquals(3,expression.getValue(new GenericMessageTestHelper2(3))); expression = parser.parseExpression("payload+2"); assertEquals(6,expression.getValue(new GenericMessageTestHelper2(4))); assertCanCompile(expression); assertEquals(8,expression.getValue(new GenericMessageTestHelper2(6))); expression = parser.parseExpression("100+payload"); assertEquals(104,expression.getValue(new GenericMessageTestHelper2(4))); assertCanCompile(expression); assertEquals(110,expression.getValue(new GenericMessageTestHelper2(10))); expression = parser.parseExpression("payload-2"); assertEquals(2,expression.getValue(new GenericMessageTestHelper2(4))); assertCanCompile(expression); assertEquals(4,expression.getValue(new GenericMessageTestHelper2(6))); expression = parser.parseExpression("100-payload"); assertEquals(96,expression.getValue(new GenericMessageTestHelper2(4))); assertCanCompile(expression); assertEquals(90,expression.getValue(new GenericMessageTestHelper2(10))); expression = parser.parseExpression("payload*2"); assertEquals(8,expression.getValue(new GenericMessageTestHelper2(4))); assertCanCompile(expression); assertEquals(12,expression.getValue(new GenericMessageTestHelper2(6))); expression = parser.parseExpression("100*payload"); assertEquals(400,expression.getValue(new GenericMessageTestHelper2(4))); assertCanCompile(expression); assertEquals(1000,expression.getValue(new GenericMessageTestHelper2(10))); } // The other numeric operators @Test public void compilerWithGenerics_12040_3() { expression = parser.parseExpression("payload >= 2"); assertTrue(expression.getValue(new GenericMessageTestHelper2(4),Boolean.TYPE)); assertCanCompile(expression); assertFalse(expression.getValue(new GenericMessageTestHelper2(1),Boolean.TYPE)); expression = parser.parseExpression("2 >= payload"); assertFalse(expression.getValue(new GenericMessageTestHelper2(5),Boolean.TYPE)); assertCanCompile(expression); assertTrue(expression.getValue(new GenericMessageTestHelper2(1),Boolean.TYPE)); expression = parser.parseExpression("payload > 2"); assertTrue(expression.getValue(new GenericMessageTestHelper2(4),Boolean.TYPE)); assertCanCompile(expression); assertFalse(expression.getValue(new GenericMessageTestHelper2(1),Boolean.TYPE)); expression = parser.parseExpression("2 > payload"); assertFalse(expression.getValue(new GenericMessageTestHelper2(5),Boolean.TYPE)); assertCanCompile(expression); assertTrue(expression.getValue(new GenericMessageTestHelper2(1),Boolean.TYPE)); expression = parser.parseExpression("payload <=2"); assertTrue(expression.getValue(new GenericMessageTestHelper2(1),Boolean.TYPE)); assertCanCompile(expression); assertFalse(expression.getValue(new GenericMessageTestHelper2(6),Boolean.TYPE)); expression = parser.parseExpression("2 <= payload"); assertFalse(expression.getValue(new GenericMessageTestHelper2(1),Boolean.TYPE)); assertCanCompile(expression); assertTrue(expression.getValue(new GenericMessageTestHelper2(6),Boolean.TYPE)); expression = parser.parseExpression("payload < 2"); assertTrue(expression.getValue(new GenericMessageTestHelper2(1),Boolean.TYPE)); assertCanCompile(expression); assertFalse(expression.getValue(new GenericMessageTestHelper2(6),Boolean.TYPE)); expression = parser.parseExpression("2 < payload"); assertFalse(expression.getValue(new GenericMessageTestHelper2(1),Boolean.TYPE)); assertCanCompile(expression); assertTrue(expression.getValue(new GenericMessageTestHelper2(6),Boolean.TYPE)); } @Test public void indexerMapAccessor_12045() throws Exception { SpelParserConfiguration spc = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,this.getClass().getClassLoader()); SpelExpressionParser sep = new SpelExpressionParser(spc); expression=sep.parseExpression("headers[command]"); MyMessage root = new MyMessage(); assertEquals("wibble",expression.getValue(root)); // This next call was failing because the isCompilable check in Indexer did not check on the key being compilable // (and also generateCode in the Indexer was missing the optimization that it didn't need necessarily need to call // generateCode for that accessor) assertEquals("wibble",expression.getValue(root)); assertCanCompile(expression); // What about a map key that is an expression - ensure the getKey() is evaluated in the right scope expression=sep.parseExpression("headers[getKey()]"); assertEquals("wobble",expression.getValue(root)); assertEquals("wobble",expression.getValue(root)); expression=sep.parseExpression("list[getKey2()]"); assertEquals("wobble",expression.getValue(root)); assertEquals("wobble",expression.getValue(root)); expression = sep.parseExpression("ia[getKey2()]"); assertEquals(3,expression.getValue(root)); assertEquals(3,expression.getValue(root)); } // --- public static interface Message { MessageHeaders getHeaders(); @SuppressWarnings("rawtypes") List getList(); int[] getIa(); } public static class MyMessage implements Message { public MessageHeaders getHeaders() { MessageHeaders mh = new MessageHeaders(); mh.put("command", "wibble"); mh.put("command2", "wobble"); return mh; } public int[] getIa() { return new int[]{5,3}; } @SuppressWarnings({ "rawtypes", "unchecked" }) public List getList() { List l = new ArrayList(); l.add("wibble"); l.add("wobble"); return l; } public String getKey() { return "command2"; } public int getKey2() { return 1; } } @SuppressWarnings("serial") public static class MessageHeaders extends HashMap { } public static class GenericMessageTestHelper { private T payload; GenericMessageTestHelper(T value) { this.payload = value; } public T getPayload() { return payload; } } // This test helper has a bound on the type variable public static class GenericMessageTestHelper2 { private T payload; GenericMessageTestHelper2(T value) { this.payload = value; } public T getPayload() { return payload; } } static class MyAccessor implements CompilablePropertyAccessor { private Method method; public Class[] getSpecificTargetClasses() { return new Class[]{Payload2.class}; } public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { // target is a Payload2 instance return true; } public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { Payload2 payload2 = (Payload2)target; return new TypedValue(payload2.getField(name)); } public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { return false; } public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { } @Override public boolean isCompilable() { return true; } @Override public Class getPropertyType() { return Object.class; } @Override public void generateCode(String propertyName, MethodVisitor mv,CodeFlow codeflow) { if (method == null) { try { method = Payload2.class.getDeclaredMethod("getField", String.class); } catch (Exception e) { } } String descriptor = codeflow.lastDescriptor(); String memberDeclaringClassSlashedDescriptor = method.getDeclaringClass().getName().replace('.','/'); if (descriptor == null) { codeflow.loadTarget(mv); } if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) { mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor); } mv.visitLdcInsn(propertyName); mv.visitMethodInsn(INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, method.getName(),CodeFlow.createSignatureDescriptor(method),false); } } static class CompilableMapAccessor implements CompilablePropertyAccessor { @Override public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { Map map = (Map) target; return map.containsKey(name); } @Override public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { Map map = (Map) target; Object value = map.get(name); if (value == null && !map.containsKey(name)) { throw new MapAccessException(name); } return new TypedValue(value); } @Override public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { return true; } @Override @SuppressWarnings("unchecked") public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { Map map = (Map) target; map.put(name, newValue); } @Override public Class[] getSpecificTargetClasses() { return new Class[] {Map.class}; } @Override public boolean isCompilable() { return true; } @Override public Class getPropertyType() { return Object.class; } @Override public void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow) { String descriptor = codeflow.lastDescriptor(); if (descriptor == null) { codeflow.loadTarget(mv); } mv.visitLdcInsn(propertyName); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get","(Ljava/lang/Object;)Ljava/lang/Object;",true); // if (method == null) { // try { // method = Payload2.class.getDeclaredMethod("getField", String.class); // } catch (Exception e) {} // } // String descriptor = codeflow.lastDescriptor(); // String memberDeclaringClassSlashedDescriptor = method.getDeclaringClass().getName().replace('.','/'); // if (descriptor == null) { // codeflow.loadTarget(mv); // } // if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) { // mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor); // } // mv.visitLdcInsn(propertyReference.getName()); // mv.visitMethodInsn(INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, method.getName(),CodeFlow.createDescriptor(method)); // 6: invokeinterface #6, 2; //InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object; } } /** * Exception thrown from {@code read} in order to reset a cached * PropertyAccessor, allowing other accessors to have a try. */ @SuppressWarnings("serial") private static class MapAccessException extends AccessException { private final String key; public MapAccessException(String key) { super(null); this.key = key; } @Override public String getMessage() { return "Map does not contain a value for key '" + this.key + "'"; } } // helpers private SpelNodeImpl getAst() { SpelExpression spelExpression = (SpelExpression)expression; SpelNode ast = spelExpression.getAST(); return (SpelNodeImpl)ast; } private String stringify(Object object) { StringBuilder s = new StringBuilder(); if (object instanceof List) { List ls = (List)object; for (Object l: ls) { s.append(l); s.append(" "); } } else if (object instanceof Object[]) { Object[] os = (Object[])object; for (Object o: os) { s.append(o); s.append(" "); } } else if (object instanceof int[]) { int[] is = (int[])object; for (int i: is) { s.append(i); s.append(" "); } } else { s.append(object.toString()); } return s.toString().trim(); } private void assertCanCompile(Expression expression) { assertTrue(SpelCompiler.compile(expression)); } private void assertCantCompile(Expression expression) { assertFalse(SpelCompiler.compile(expression)); } private Expression parse(String expression) { return parser.parseExpression(expression); } private void assertGetValueFail(Expression expression) { try { Object o = expression.getValue(); fail("Calling getValue on the expression should have failed but returned "+o); } catch (Exception ex) { // success! } } // test classes public static class Payload { Two[] DR = new Two[]{new Two()}; public Two holder = new Two(); public Two[] getDR() { return DR; } } public static class Payload2 { String var1 = "abc"; String var2 = "def"; public Object getField(String name) { if (name.equals("var1")) { return var1; } else if (name.equals("var2")) { return var2; } return null; } } public static class Payload2Holder { public Payload2 payload2 = new Payload2(); } public static class Two { Three three = new Three(); public Three getThree() { return three; } public String toString() { return "instanceof Two"; } } public static class Three { double four = 0.04d; public double getFour() { return four; } } public static class TestClass1 { public int index1 = 1; public int index2 = 3; public String word = "abcd"; } public static class TestClass4 { public boolean a,b; public boolean gettrue() { return true; } public boolean getfalse() { return false; } public boolean getA() { return a; } public boolean getB() { return b; } } public static class TestClass5 { public int i = 0; public String s = null; public static int _i = 0; public static String _s = null; public Object obj = null; public String field = null; public void reset() { i = 0; _i=0; s = null; _s = null; field = null; } public void one() { i = 1; } public static void two() { _i = 1; } public String three() { return "hello"; } public long four() { return 3277700L; } public static String five() { return "hello"; } public static long six() { return 3277700L; } public void seven(String toset) { s = toset; } // public void seven(Number n) { s = n.toString(); } public void takeNumber(Number n) { s = n.toString(); } public void takeString(String s) { this.s = s; } public static void eight(String toset) { _s = toset; } public void nine(int toset) { i = toset; } public static void ten(int toset) { _i = toset; } public void eleven(String... vargs) { if (vargs==null) { s = ""; } else { s = ""; for (String varg: vargs) { s+=varg; } } } public void twelve(int... vargs) { if (vargs==null) { i = 0; } else { i = 0; for (int varg: vargs) { i+=varg; } } } } public static class TestClass6 { public String orange = "value1"; public static String apple = "value2"; public long peach = 34L; public String getBanana() { return "value3"; } public static String getPlum() { return "value4"; } } public static class TestClass7 { public static String property; static { String s = "UK 123"; StringTokenizer st = new StringTokenizer(s); property = st.nextToken(); } public static void reset() { String s = "UK 123"; StringTokenizer st = new StringTokenizer(s); property = st.nextToken(); } } public static class TestClass8 { public int i; public String s; public double d; public boolean z; public TestClass8(int i, String s, double d, boolean z) { this.i = i; this.s = s; this.d = d; this.z = z; } public TestClass8() { } public TestClass8(Integer i) { this.i = i; } @SuppressWarnings("unused") private TestClass8(String a, String b) { this.s = a+b; } } @SuppressWarnings("unused") private static class TestClass9 { public TestClass9(int i) {} } }