Rename modules {org.springframework.*=>spring-*}

This renaming more intuitively expresses the relationship between
subprojects and the JAR artifacts they produce.

Tracking history across these renames is possible, but it requires
use of the --follow flag to `git log`, for example

    $ git log spring-aop/src/main/java/org/springframework/aop/Advisor.java

will show history up until the renaming event, where

    $ git log --follow spring-aop/src/main/java/org/springframework/aop/Advisor.java

will show history for all changes to the file, before and after the
renaming.

See http://chrisbeams.com/git-diff-across-renamed-directories
This commit is contained in:
Chris Beams
2012-01-20 22:51:02 +01:00
parent b6cb514d38
commit 02a4473c62
5671 changed files with 20 additions and 32 deletions

View File

@@ -0,0 +1,195 @@
/*
* Copyright 2002-2010 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 org.junit.Assert;
import org.junit.Test;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* Test construction of arrays.
*
* @author Andy Clement
*/
public class ArrayConstructorTests extends ExpressionTestCase {
@Test
public void testSimpleArrayWithInitializer() {
evaluateArrayBuildingExpression("new int[]{1,2,3}", "[1,2,3]");
evaluateArrayBuildingExpression("new int[]{}", "[]");
evaluate("new int[]{}.length", "0", Integer.class);
}
@Test
public void testConversion() {
evaluate("new String[]{1,2,3}[0]", "1", String.class);
evaluate("new int[]{'123'}[0]", 123, Integer.class);
}
@Test
public void testMultidimensionalArrays() {
evaluateAndCheckError("new int[][]{{1,2},{3,4}}", SpelMessage.MULTIDIM_ARRAY_INITIALIZER_NOT_SUPPORTED);
evaluateAndCheckError("new int[3][]", SpelMessage.MISSING_ARRAY_DIMENSION);
evaluateAndCheckError("new int[]", SpelMessage.MISSING_ARRAY_DIMENSION);
evaluateAndCheckError("new String[]", SpelMessage.MISSING_ARRAY_DIMENSION);
evaluateAndCheckError("new int[][1]", SpelMessage.MISSING_ARRAY_DIMENSION);
}
@Test
public void testPrimitiveTypeArrayConstructors() {
evaluateArrayBuildingExpression("new int[]{1,2,3,4}", "[1,2,3,4]");
evaluateArrayBuildingExpression("new boolean[]{true,false,true}", "[true,false,true]");
evaluateArrayBuildingExpression("new char[]{'a','b','c'}", "[a,b,c]");
evaluateArrayBuildingExpression("new long[]{1,2,3,4,5}", "[1,2,3,4,5]");
evaluateArrayBuildingExpression("new short[]{2,3,4,5,6}", "[2,3,4,5,6]");
evaluateArrayBuildingExpression("new double[]{1d,2d,3d,4d}", "[1.0,2.0,3.0,4.0]");
evaluateArrayBuildingExpression("new float[]{1f,2f,3f,4f}", "[1.0,2.0,3.0,4.0]");
evaluateArrayBuildingExpression("new byte[]{1,2,3,4}", "[1,2,3,4]");
}
@Test
public void testPrimitiveTypeArrayConstructorsElements() {
evaluate("new int[]{1,2,3,4}[0]", 1, Integer.class);
evaluate("new boolean[]{true,false,true}[0]", true, Boolean.class);
evaluate("new char[]{'a','b','c'}[0]", 'a', Character.class);
evaluate("new long[]{1,2,3,4,5}[0]", 1L, Long.class);
evaluate("new short[]{2,3,4,5,6}[0]", (short) 2, Short.class);
evaluate("new double[]{1d,2d,3d,4d}[0]", (double) 1, Double.class);
evaluate("new float[]{1f,2f,3f,4f}[0]", (float) 1, Float.class);
evaluate("new byte[]{1,2,3,4}[0]", (byte) 1, Byte.class);
evaluate("new String(new char[]{'h','e','l','l','o'})", "hello", String.class);
}
@Test
public void testErrorCases() {
evaluateAndCheckError("new char[7]{'a','c','d','e'}", SpelMessage.INITIALIZER_LENGTH_INCORRECT);
evaluateAndCheckError("new char[3]{'a','c','d','e'}", SpelMessage.INITIALIZER_LENGTH_INCORRECT);
evaluateAndCheckError("new char[2]{'hello','world'}", SpelMessage.TYPE_CONVERSION_ERROR);
evaluateAndCheckError("new String('a','c','d')", SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM);
}
@Test
public void testTypeArrayConstructors() {
evaluate("new String[]{'a','b','c','d'}[1]", "b", String.class);
evaluateAndCheckError("new String[]{'a','b','c','d'}.size()", SpelMessage.METHOD_NOT_FOUND, 30, "size()",
"java.lang.String[]");
evaluate("new String[]{'a','b','c','d'}.length", 4, Integer.class);
}
@Test
public void testBasicArray() {
evaluate("new String[3]", "java.lang.String[3]{null,null,null}", String[].class);
}
@Test
public void testMultiDimensionalArray() {
evaluate("new String[2][2]", "[Ljava.lang.String;[2]{[2]{null,null},[2]{null,null}}", String[][].class);
evaluate("new String[3][2][1]",
"[[Ljava.lang.String;[3]{[2]{[1]{null},[1]{null}},[2]{[1]{null},[1]{null}},[2]{[1]{null},[1]{null}}}",
String[][][].class);
}
@Test
public void testConstructorInvocation03() {
evaluateAndCheckError("new String[]", SpelMessage.MISSING_ARRAY_DIMENSION);
}
public void testConstructorInvocation04() {
evaluateAndCheckError("new Integer[3]{'3','ghi','5'}", SpelMessage.INCORRECT_ELEMENT_TYPE_FOR_ARRAY, 4);
}
private String evaluateArrayBuildingExpression(String expression, String expectedToString) {
SpelExpressionParser parser = new SpelExpressionParser();
Expression e = parser.parseExpression(expression);
Object o = e.getValue();
Assert.assertNotNull(o);
Assert.assertTrue(o.getClass().isArray());
StringBuilder s = new StringBuilder();
s.append('[');
if (o instanceof int[]) {
int[] array = (int[]) o;
for (int i = 0; i < array.length; i++) {
if (i > 0) {
s.append(',');
}
s.append(array[i]);
}
} else if (o instanceof boolean[]) {
boolean[] array = (boolean[]) o;
for (int i = 0; i < array.length; i++) {
if (i > 0) {
s.append(',');
}
s.append(array[i]);
}
} else if (o instanceof char[]) {
char[] array = (char[]) o;
for (int i = 0; i < array.length; i++) {
if (i > 0) {
s.append(',');
}
s.append(array[i]);
}
} else if (o instanceof long[]) {
long[] array = (long[]) o;
for (int i = 0; i < array.length; i++) {
if (i > 0) {
s.append(',');
}
s.append(array[i]);
}
} else if (o instanceof short[]) {
short[] array = (short[]) o;
for (int i = 0; i < array.length; i++) {
if (i > 0) {
s.append(',');
}
s.append(array[i]);
}
} else if (o instanceof double[]) {
double[] array = (double[]) o;
for (int i = 0; i < array.length; i++) {
if (i > 0) {
s.append(',');
}
s.append(array[i]);
}
} else if (o instanceof float[]) {
float[] array = (float[]) o;
for (int i = 0; i < array.length; i++) {
if (i > 0) {
s.append(',');
}
s.append(array[i]);
}
} else if (o instanceof byte[]) {
byte[] array = (byte[]) o;
for (int i = 0; i < array.length; i++) {
if (i > 0) {
s.append(',');
}
s.append(array[i]);
}
} else {
Assert.fail("Not supported " + o.getClass());
}
s.append(']');
Assert.assertEquals(expectedToString, s.toString());
return s.toString();
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2002-2009 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 org.junit.Test;
/**
* Tests the evaluation of real boolean expressions, these use AND, OR, NOT, TRUE, FALSE
*
* @author Andy Clement
*/
public class BooleanExpressionTests extends ExpressionTestCase {
@Test
public void testBooleanTrue() {
evaluate("true", Boolean.TRUE, Boolean.class);
}
@Test
public void testBooleanFalse() {
evaluate("false", Boolean.FALSE, Boolean.class);
}
@Test
public void testOr() {
evaluate("false or false", Boolean.FALSE, Boolean.class);
evaluate("false or true", Boolean.TRUE, Boolean.class);
evaluate("true or false", Boolean.TRUE, Boolean.class);
evaluate("true or true", Boolean.TRUE, Boolean.class);
}
@Test
public void testAnd() {
evaluate("false and false", Boolean.FALSE, Boolean.class);
evaluate("false and true", Boolean.FALSE, Boolean.class);
evaluate("true and false", Boolean.FALSE, Boolean.class);
evaluate("true and true", Boolean.TRUE, Boolean.class);
}
@Test
public void testNot() {
evaluate("!false", Boolean.TRUE, Boolean.class);
evaluate("!true", Boolean.FALSE, Boolean.class);
evaluate("not false", Boolean.TRUE, Boolean.class);
evaluate("NoT true", Boolean.FALSE, Boolean.class);
}
@Test
public void testCombinations01() {
evaluate("false and false or true", Boolean.TRUE, Boolean.class);
evaluate("true and false or true", Boolean.TRUE, Boolean.class);
evaluate("true and false or false", Boolean.FALSE, Boolean.class);
}
@Test
public void testWritability() {
evaluate("true and true", Boolean.TRUE, Boolean.class, false);
evaluate("true or true", Boolean.TRUE, Boolean.class, false);
evaluate("!false", Boolean.TRUE, Boolean.class, false);
}
@Test
public void testBooleanErrors01() {
evaluateAndCheckError("1.0 or false", SpelMessage.TYPE_CONVERSION_ERROR, 0);
evaluateAndCheckError("false or 39.4", SpelMessage.TYPE_CONVERSION_ERROR, 9);
evaluateAndCheckError("true and 'hello'", SpelMessage.TYPE_CONVERSION_ERROR, 9);
evaluateAndCheckError(" 'hello' and 'goodbye'", SpelMessage.TYPE_CONVERSION_ERROR, 1);
evaluateAndCheckError("!35.2", SpelMessage.TYPE_CONVERSION_ERROR, 1);
evaluateAndCheckError("! 'foob'", SpelMessage.TYPE_CONVERSION_ERROR, 2);
}
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright 2002-2009 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.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.ConstructorExecutor;
import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.testresources.PlaceOfBirth;
/**
* Tests invocation of constructors.
*
* @author Andy Clement
*/
public class ConstructorInvocationTests extends ExpressionTestCase {
@Test
public void testTypeConstructors() {
evaluate("new String('hello world')", "hello world", String.class);
}
@Test
public void testNonExistentType() {
evaluateAndCheckError("new FooBar()",SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM);
}
static class TestException extends Exception {
}
static class Tester {
public static int counter;
public int i;
public Tester() {}
public Tester(int i) throws Exception {
counter++;
if (i==1) {
throw new IllegalArgumentException("IllegalArgumentException for 1");
}
if (i==2) {
throw new RuntimeException("RuntimeException for 2");
}
if (i==4) {
throw new TestException();
}
this.i = i;
}
public Tester(PlaceOfBirth pob) {
}
}
@Test
public void testConstructorThrowingException_SPR6760() {
// Test ctor on inventor:
// On 1 it will throw an IllegalArgumentException
// On 2 it will throw a RuntimeException
// On 3 it will exit normally
// In each case it increments the Tester field 'counter' when invoked
SpelExpressionParser parser = new SpelExpressionParser();
Expression expr = parser.parseExpression("new org.springframework.expression.spel.ConstructorInvocationTests$Tester(#bar).i");
// Normal exit
StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
eContext.setRootObject(new Tester());
eContext.setVariable("bar",3);
Object o = expr.getValue(eContext);
Assert.assertEquals(o,3);
Assert.assertEquals(1,parser.parseExpression("counter").getValue(eContext));
// Now the expression has cached that throwException(int) is the right thing to call
// Let's change 'bar' to be a PlaceOfBirth which indicates the cached reference is
// out of date.
eContext.setVariable("bar",new PlaceOfBirth("London"));
o = expr.getValue(eContext);
Assert.assertEquals(0, o);
// That confirms the logic to mark the cached reference stale and retry is working
// Now let's cause the method to exit via exception and ensure it doesn't cause
// a retry.
// First, switch back to throwException(int)
eContext.setVariable("bar",3);
o = expr.getValue(eContext);
Assert.assertEquals(3, o);
Assert.assertEquals(2,parser.parseExpression("counter").getValue(eContext));
// 4 will make it throw a checked exception - this will be wrapped by spel on the way out
eContext.setVariable("bar",4);
try {
o = expr.getValue(eContext);
Assert.fail("Should have failed");
} catch (Exception e) {
// A problem occurred whilst attempting to construct an object of type 'org.springframework.expression.spel.ConstructorInvocationTests$Tester' using arguments '(java.lang.Integer)'
int idx = e.getMessage().indexOf("Tester");
if (idx==-1) {
Assert.fail("Expected reference to Tester in :"+e.getMessage());
}
// normal
}
// If counter is 4 then the method got called twice!
Assert.assertEquals(3,parser.parseExpression("counter").getValue(eContext));
// 1 will make it throw a RuntimeException - SpEL will let this through
eContext.setVariable("bar",1);
try {
o = expr.getValue(eContext);
Assert.fail("Should have failed");
} catch (Exception e) {
// A problem occurred whilst attempting to construct an object of type 'org.springframework.expression.spel.ConstructorInvocationTests$Tester' using arguments '(java.lang.Integer)'
if (e instanceof SpelEvaluationException) {
e.printStackTrace();
Assert.fail("Should not have been wrapped");
}
}
// If counter is 5 then the method got called twice!
Assert.assertEquals(4,parser.parseExpression("counter").getValue(eContext));
}
@Test
public void testAddingConstructorResolvers() {
StandardEvaluationContext ctx = new StandardEvaluationContext();
// reflective constructor accessor is the only one by default
List<ConstructorResolver> constructorResolvers = ctx.getConstructorResolvers();
Assert.assertEquals(1,constructorResolvers.size());
ConstructorResolver dummy = new DummyConstructorResolver();
ctx.addConstructorResolver(dummy);
Assert.assertEquals(2,ctx.getConstructorResolvers().size());
List<ConstructorResolver> copy = new ArrayList<ConstructorResolver>();
copy.addAll(ctx.getConstructorResolvers());
Assert.assertTrue(ctx.removeConstructorResolver(dummy));
Assert.assertFalse(ctx.removeConstructorResolver(dummy));
Assert.assertEquals(1,ctx.getConstructorResolvers().size());
ctx.setConstructorResolvers(copy);
Assert.assertEquals(2,ctx.getConstructorResolvers().size());
}
static class DummyConstructorResolver implements ConstructorResolver {
public ConstructorExecutor resolve(EvaluationContext context, String typeName, List<TypeDescriptor> argumentTypes)
throws AccessException {
throw new UnsupportedOperationException("Auto-generated method stub");
}
}
@Test
public void testVarargsInvocation01() {
// Calling 'Fruit(String... strings)'
evaluate("new org.springframework.expression.spel.testresources.Fruit('a','b','c').stringscount()", 3, Integer.class);
evaluate("new org.springframework.expression.spel.testresources.Fruit('a').stringscount()", 1, Integer.class);
evaluate("new org.springframework.expression.spel.testresources.Fruit().stringscount()", 0, Integer.class);
evaluate("new org.springframework.expression.spel.testresources.Fruit(1,2,3).stringscount()", 3, Integer.class); // all need converting to strings
evaluate("new org.springframework.expression.spel.testresources.Fruit(1).stringscount()", 1, Integer.class); // needs string conversion
evaluate("new org.springframework.expression.spel.testresources.Fruit(1,'a',3.0d).stringscount()", 3, Integer.class); // first and last need conversion
}
@Test
public void testVarargsInvocation02() {
// Calling 'Fruit(int i, String... strings)' - returns int+length_of_strings
evaluate("new org.springframework.expression.spel.testresources.Fruit(5,'a','b','c').stringscount()", 8, Integer.class);
evaluate("new org.springframework.expression.spel.testresources.Fruit(2,'a').stringscount()", 3, Integer.class);
evaluate("new org.springframework.expression.spel.testresources.Fruit(4).stringscount()", 4, Integer.class);
evaluate("new org.springframework.expression.spel.testresources.Fruit(8,2,3).stringscount()", 10, Integer.class);
evaluate("new org.springframework.expression.spel.testresources.Fruit(9).stringscount()", 9, Integer.class);
evaluate("new org.springframework.expression.spel.testresources.Fruit(2,'a',3.0d).stringscount()", 4, Integer.class);
evaluate("new org.springframework.expression.spel.testresources.Fruit(8,stringArrayOfThreeItems).stringscount()", 11, Integer.class);
}
/*
* These tests are attempting to call constructors where we need to widen or convert the argument in order to
* satisfy a suitable constructor.
*/
@Test
public void testWidening01() {
// widening of int 3 to double 3 is OK
evaluate("new Double(3)", 3.0d, Double.class);
// widening of int 3 to long 3 is OK
evaluate("new Long(3)", 3L, Long.class);
}
@Test
@Ignore
public void testArgumentConversion01() {
// Closest ctor will be new String(String) and converter supports Double>String
// TODO currently failing as with new ObjectToArray converter closest constructor matched becomes String(byte[]) which fails...
evaluate("new String(3.0d)", "3.0", String.class);
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2004-2008 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 junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.spel.support.StandardTypeComparator;
/**
* Unit tests for type comparison
*
* @author Andy Clement
*/
public class DefaultComparatorUnitTests {
@Test
public void testPrimitives() throws EvaluationException {
TypeComparator comparator = new StandardTypeComparator();
// primitive int
Assert.assertTrue(comparator.compare(1, 2) < 0);
Assert.assertTrue(comparator.compare(1, 1) == 0);
Assert.assertTrue(comparator.compare(2, 1) > 0);
Assert.assertTrue(comparator.compare(1.0d, 2) < 0);
Assert.assertTrue(comparator.compare(1.0d, 1) == 0);
Assert.assertTrue(comparator.compare(2.0d, 1) > 0);
Assert.assertTrue(comparator.compare(1.0f, 2) < 0);
Assert.assertTrue(comparator.compare(1.0f, 1) == 0);
Assert.assertTrue(comparator.compare(2.0f, 1) > 0);
Assert.assertTrue(comparator.compare(1L, 2) < 0);
Assert.assertTrue(comparator.compare(1L, 1) == 0);
Assert.assertTrue(comparator.compare(2L, 1) > 0);
Assert.assertTrue(comparator.compare(1, 2L) < 0);
Assert.assertTrue(comparator.compare(1, 1L) == 0);
Assert.assertTrue(comparator.compare(2, 1L) > 0);
Assert.assertTrue(comparator.compare(1L, 2L) < 0);
Assert.assertTrue(comparator.compare(1L, 1L) == 0);
Assert.assertTrue(comparator.compare(2L, 1L) > 0);
}
@Test
public void testNulls() throws EvaluationException {
TypeComparator comparator = new StandardTypeComparator();
Assert.assertTrue(comparator.compare(null,"abc")<0);
Assert.assertTrue(comparator.compare(null,null)==0);
Assert.assertTrue(comparator.compare("abc",null)>0);
}
@Test
public void testObjects() throws EvaluationException {
TypeComparator comparator = new StandardTypeComparator();
Assert.assertTrue(comparator.compare("a","a")==0);
Assert.assertTrue(comparator.compare("a","b")<0);
Assert.assertTrue(comparator.compare("b","a")>0);
}
@Test
public void testCanCompare() throws EvaluationException {
TypeComparator comparator = new StandardTypeComparator();
Assert.assertTrue(comparator.canCompare(null,1));
Assert.assertTrue(comparator.canCompare(1,null));
Assert.assertTrue(comparator.canCompare(2,1));
Assert.assertTrue(comparator.canCompare("abc","def"));
Assert.assertTrue(comparator.canCompare("abc",3));
Assert.assertFalse(comparator.canCompare(String.class,3));
}
}

View File

@@ -0,0 +1,561 @@
/*
* Copyright 2002-2010 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.util.List;
import java.util.Map;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeLocator;
import org.springframework.expression.spel.testresources.TestPerson;
/**
* Tests the evaluation of real expressions in a real context.
*
* @author Andy Clement
* @author Mark Fisher
* @since 3.0
*/
public class EvaluationTests extends ExpressionTestCase {
@Test
public void testCreateListsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
Expression expression = parser.parseExpression("list[0]");
TestClass testClass = new TestClass();
Object o = null;
o = expression.getValue(new StandardEvaluationContext(testClass));
Assert.assertEquals("",o);
o = parser.parseExpression("list[3]").getValue(new StandardEvaluationContext(testClass));
Assert.assertEquals("",o);
Assert.assertEquals(4, testClass.list.size());
try {
o = parser.parseExpression("list2[3]").getValue(new StandardEvaluationContext(testClass));
Assert.fail();
} catch (EvaluationException ee) {
ee.printStackTrace();
// success!
}
o = parser.parseExpression("foo[3]").getValue(new StandardEvaluationContext(testClass));
Assert.assertEquals("",o);
Assert.assertEquals(4, testClass.getFoo().size());
}
@Test
public void testCreateMapsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
TestClass testClass = new TestClass();
StandardEvaluationContext ctx = new StandardEvaluationContext(testClass);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
Object o = null;
o = parser.parseExpression("map['a']").getValue(ctx);
Assert.assertNull(o);
o = parser.parseExpression("map").getValue(ctx);
Assert.assertNotNull(o);
try {
o = parser.parseExpression("map2['a']").getValue(ctx);
// fail!
Assert.fail("map2 should be null, there is no setter");
} catch (Exception e) {
// success!
}
}
@Test
public void testCreateObjectsOnAttemptToReferenceNull() throws EvaluationException, ParseException {
TestClass testClass = new TestClass();
StandardEvaluationContext ctx = new StandardEvaluationContext(testClass);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
Object o = null;
o = parser.parseExpression("wibble.bar").getValue(ctx);
Assert.assertEquals("hello",o);
o = parser.parseExpression("wibble").getValue(ctx);
Assert.assertNotNull(o);
try {
o = parser.parseExpression("wibble2.bar").getValue(ctx);
// fail!
Assert.fail("wibble2 should be null (cannot be initialized dynamically), there is no setter");
} catch (Exception e) {
// success!
}
}
static class TestClass {
public Foo wibble;
private Foo wibble2;
public Map map;
public Map<String,Integer> mapStringToInteger;
public List<String> list;
public List list2;
private Map map2;
public Map getMap2() { return this.map2; }
public Foo getWibble2() { return this.wibble2; }
// public void setMap2(Map m) { this.map2 = m; }
private List<String> foo;
public List<String> getFoo() { return this.foo; }
public void setFoo(List<String> newfoo) { this.foo = newfoo; }
}
public static class Foo {
public Foo() {}
public String bar = "hello";
}
@Test
public void testElvis01() {
evaluate("'Andy'?:'Dave'","Andy",String.class);
evaluate("null?:'Dave'","Dave",String.class);
}
@Test
public void testSafeNavigation() {
evaluate("null?.null?.null",null,null);
}
@Test
public void testRelOperatorGT01() {
evaluate("3 > 6", "false", Boolean.class);
}
@Test
public void testRelOperatorLT01() {
evaluate("3 < 6", "true", Boolean.class);
}
@Test
public void testRelOperatorLE01() {
evaluate("3 <= 6", "true", Boolean.class);
}
@Test
public void testRelOperatorGE01() {
evaluate("3 >= 6", "false", Boolean.class);
}
@Test
public void testRelOperatorGE02() {
evaluate("3 >= 3", "true", Boolean.class);
}
@Test
public void testRelOperatorsInstanceof01() {
evaluate("'xyz' instanceof T(int)", "false", Boolean.class);
}
@Test
public void testRelOperatorsInstanceof04() {
evaluate("null instanceof T(String)", "false", Boolean.class);
}
@Test
public void testRelOperatorsInstanceof05() {
evaluate("null instanceof T(Integer)", "false", Boolean.class);
}
@Test
public void testRelOperatorsInstanceof06() {
evaluateAndCheckError("'A' instanceof null", SpelMessage.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND, 15, "null");
}
@Test
public void testRelOperatorsMatches01() {
evaluate("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "false", Boolean.class);
}
@Test
public void testRelOperatorsMatches02() {
evaluate("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "true", Boolean.class);
}
@Test
public void testRelOperatorsMatches03() {
evaluateAndCheckError("null matches '^.*$'", SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, 0, null);
}
@Test
public void testRelOperatorsMatches04() {
evaluateAndCheckError("'abc' matches null", SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, 14, null);
}
@Test
public void testRelOperatorsMatches05() {
evaluate("27 matches '^.*2.*$'", true, Boolean.class); // conversion int>string
}
// mixing operators
@Test
public void testMixingOperators01() {
evaluate("true and 5>3", "true", Boolean.class);
}
// property access
@Test
public void testPropertyField01() {
evaluate("name", "Nikola Tesla", String.class, false);
// not writable because (1) name is private (2) there is no setter, only a getter
evaluateAndCheckError("madeup", SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, 0, "madeup",
"org.springframework.expression.spel.testresources.Inventor");
}
@Test
public void testPropertyField02_SPR7100() {
evaluate("_name", "Nikola Tesla", String.class);
evaluate("_name_", "Nikola Tesla", String.class);
}
@Test
public void testRogueTrailingDotCausesNPE_SPR6866() {
try {
new SpelExpressionParser().parseExpression("placeOfBirth.foo.");
Assert.fail("Should have failed to parse");
} catch (ParseException e) {
Assert.assertTrue(e instanceof SpelParseException);
SpelParseException spe = (SpelParseException)e;
Assert.assertEquals(SpelMessage.OOD,spe.getMessageCode());
Assert.assertEquals(16,spe.getPosition());
}
}
// nested properties
@Test
public void testPropertiesNested01() {
evaluate("placeOfBirth.city", "SmilJan", String.class, true);
}
@Test
public void testPropertiesNested02() {
evaluate("placeOfBirth.doubleIt(12)", "24", Integer.class);
}
@Test
public void testPropertiesNested03() throws ParseException {
try {
new SpelExpressionParser().parseRaw("placeOfBirth.23");
Assert.fail();
} catch (SpelParseException spe) {
Assert.assertEquals(spe.getMessageCode(), SpelMessage.UNEXPECTED_DATA_AFTER_DOT);
Assert.assertEquals("23", spe.getInserts()[0]);
}
}
// methods
@Test
public void testMethods01() {
evaluate("echo(12)", "12", String.class);
}
@Test
public void testMethods02() {
evaluate("echo(name)", "Nikola Tesla", String.class);
}
// constructors
@Test
public void testConstructorInvocation01() {
evaluate("new String('hello')", "hello", String.class);
}
@Test
public void testConstructorInvocation05() {
evaluate("new java.lang.String('foobar')", "foobar", String.class);
}
@Test
public void testConstructorInvocation06() throws Exception {
// repeated evaluation to drive use of cached executor
SpelExpression expr = (SpelExpression)parser.parseExpression("new String('wibble')");
String newString = expr.getValue(String.class);
Assert.assertEquals("wibble",newString);
newString = expr.getValue(String.class);
Assert.assertEquals("wibble",newString);
// not writable
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
// ast
Assert.assertEquals("new String('wibble')",expr.toStringAST());
}
// unary expressions
@Test
public void testUnaryMinus01() {
evaluate("-5", "-5", Integer.class);
}
@Test
public void testUnaryPlus01() {
evaluate("+5", "5", Integer.class);
}
@Test
public void testUnaryNot01() {
evaluate("!true", "false", Boolean.class);
}
@Test
public void testUnaryNot02() {
evaluate("!false", "true", Boolean.class);
}
@Test(expected = EvaluationException.class)
public void testUnaryNotWithNullValue() {
parser.parseExpression("!null").getValue();
}
@Test(expected = EvaluationException.class)
public void testAndWithNullValueOnLeft() {
parser.parseExpression("null and true").getValue();
}
@Test(expected = EvaluationException.class)
public void testAndWithNullValueOnRight() {
parser.parseExpression("true and null").getValue();
}
@Test(expected = EvaluationException.class)
public void testOrWithNullValueOnLeft() {
parser.parseExpression("null or false").getValue();
}
@Test(expected = EvaluationException.class)
public void testOrWithNullValueOnRight() {
parser.parseExpression("false or null").getValue();
}
// assignment
@Test
public void testAssignmentToVariables01() {
evaluate("#var1='value1'", "value1", String.class);
}
@Test
public void testTernaryOperator01() {
evaluate("2>4?1:2",2,Integer.class);
}
@Test
public void testTernaryOperator02() {
evaluate("'abc'=='abc'?1:2",1,Integer.class);
}
@Test
public void testTernaryOperator03() {
evaluateAndCheckError("'hello'?1:2", SpelMessage.TYPE_CONVERSION_ERROR); // cannot convert String to boolean
}
@Test
public void testTernaryOperator04() throws Exception {
Expression expr = parser.parseExpression("1>2?3:4");
Assert.assertFalse(expr.isWritable(eContext));
}
@Test
public void testTernaryOperator05() {
evaluate("1>2?#var=4:#var=5",5,Integer.class);
evaluate("3?:#var=5",3,Integer.class);
evaluate("null?:#var=5",5,Integer.class);
evaluate("2>4?(3>2?true:false):(5<3?true:false)",false,Boolean.class);
}
@Test(expected = EvaluationException.class)
public void testTernaryOperatorWithNullValue() {
parser.parseExpression("null ? 0 : 1").getValue();
}
@Test
public void methodCallWithRootReferenceThroughParameter() {
evaluate("placeOfBirth.doubleIt(inventions.length)", 18, Integer.class);
}
@Test
public void ctorCallWithRootReferenceThroughParameter() {
evaluate("new org.springframework.expression.spel.testresources.PlaceOfBirth(inventions[0].toString()).city", "Telephone repeater", String.class);
}
@Test
public void fnCallWithRootReferenceThroughParameter() {
evaluate("#reverseInt(inventions.length, inventions.length, inventions.length)", "int[3]{9,9,9}", int[].class);
}
@Test
public void methodCallWithRootReferenceThroughParameterThatIsAFunctionCall() {
evaluate("placeOfBirth.doubleIt(#reverseInt(inventions.length,2,3)[2])", 18, Integer.class);
}
@Test
public void testIndexer03() {
evaluate("'christian'[8]", "n", String.class);
}
@Test
public void testIndexerError() {
evaluateAndCheckError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
}
@Test
public void testStaticRef02() {
evaluate("T(java.awt.Color).green.getRGB()!=0", "true", Boolean.class);
}
// variables and functions
@Test
public void testVariableAccess01() {
evaluate("#answer", "42", Integer.class, true);
}
@Test
public void testFunctionAccess01() {
evaluate("#reverseInt(1,2,3)", "int[3]{3,2,1}", int[].class);
}
@Test
public void testFunctionAccess02() {
evaluate("#reverseString('hello')", "olleh", String.class);
}
// type references
@Test
public void testTypeReferences01() {
evaluate("T(java.lang.String)", "class java.lang.String", Class.class);
}
@Test
public void testTypeReferencesAndQualifiedIdentifierCaching() throws Exception {
SpelExpression expr = (SpelExpression)parser.parseExpression("T(java.lang.String)");
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
Assert.assertEquals("T(java.lang.String)",expr.toStringAST());
Assert.assertEquals(String.class,expr.getValue(Class.class));
// use cached QualifiedIdentifier:
Assert.assertEquals("T(java.lang.String)",expr.toStringAST());
Assert.assertEquals(String.class,expr.getValue(Class.class));
}
@Test
public void testTypeReferencesPrimitive() {
evaluate("T(int)", "int", Class.class);
evaluate("T(byte)", "byte", Class.class);
evaluate("T(char)", "char", Class.class);
evaluate("T(boolean)", "boolean", Class.class);
evaluate("T(long)", "long", Class.class);
evaluate("T(short)", "short", Class.class);
evaluate("T(double)", "double", Class.class);
evaluate("T(float)", "float", Class.class);
}
@Test
public void testTypeReferences02() {
evaluate("T(String)", "class java.lang.String", Class.class);
}
@Test
public void testStringType() {
evaluateAndAskForReturnType("getPlaceOfBirth().getCity()", "SmilJan", String.class);
}
@Test
public void testNumbers01() {
evaluateAndAskForReturnType("3*4+5", 17, Integer.class);
evaluateAndAskForReturnType("3*4+5", 17L, Long.class);
evaluateAndAskForReturnType("65", 'A', Character.class);
evaluateAndAskForReturnType("3*4+5", (short) 17, Short.class);
evaluateAndAskForReturnType("3*4+5", "17", String.class);
}
@Test
public void testAdvancedNumerics() throws Exception {
int twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Integer.class);
Assert.assertEquals(24,twentyFour);
double one = parser.parseExpression("8.0 / 5e0 % 2").getValue(Double.class);
Assert.assertEquals(1.6d,one);
int o = parser.parseExpression("8.0 / 5e0 % 2").getValue(Integer.class);
Assert.assertEquals(1,o);
int sixteen = parser.parseExpression("-2 ^ 4").getValue(Integer.class);
Assert.assertEquals(16,sixteen);
int minusFortyFive = parser.parseExpression("1+2-3*8^2/2/2").getValue(Integer.class);
Assert.assertEquals(-45,minusFortyFive);
}
@Test
public void testComparison() throws Exception {
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
boolean trueValue = parser.parseExpression("T(java.util.Date) == Birthdate.Class").getValue(context, Boolean.class);
Assert.assertTrue(trueValue);
}
@Test
public void testResolvingList() throws Exception {
StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
try {
Assert.assertFalse(parser.parseExpression("T(List)!=null").getValue(context, Boolean.class));
Assert.fail("should have failed to find List");
} catch (EvaluationException ee) {
// success - List not found
}
((StandardTypeLocator)context.getTypeLocator()).registerImport("java.util");
Assert.assertTrue(parser.parseExpression("T(List)!=null").getValue(context, Boolean.class));
}
@Test
public void testResolvingString() throws Exception {
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
Assert.assertEquals(String.class,stringClass);
}
/**
* SPR-6984: attempting to index a collection on write using an index that doesn't currently exist in the collection (address.crossStreets[0] below)
*/
@Test
public void initializingCollectionElementsOnWrite() throws Exception {
TestPerson person = new TestPerson();
EvaluationContext context = new StandardEvaluationContext(person);
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
ExpressionParser parser = new SpelExpressionParser(config);
Expression expression = parser.parseExpression("name");
expression.setValue(context, "Oleg");
Assert.assertEquals("Oleg",person.getName());
expression = parser.parseExpression("address.street");
expression.setValue(context, "123 High St");
Assert.assertEquals("123 High St",person.getAddress().getStreet());
expression = parser.parseExpression("address.crossStreets[0]");
expression.setValue(context, "Blah");
Assert.assertEquals("Blah",person.getAddress().getCrossStreets().get(0));
expression = parser.parseExpression("address.crossStreets[3]");
expression.setValue(context, "Wibble");
Assert.assertEquals("Blah",person.getAddress().getCrossStreets().get(0));
Assert.assertEquals("Wibble",person.getAddress().getCrossStreets().get(3));
}
}

View File

@@ -0,0 +1,318 @@
/*
* Copyright 2002-2009 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.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
///CLOVER:OFF
/**
* Testcases showing the common scenarios/use-cases for picking up the expression language support.
* The first test shows very basic usage, just drop it in and go. By 'standard infrastructure', it means:<br>
* <ul>
* <li>The context classloader is used (so, the default classpath)
* <li>Some basic type converters are included
* <li>properties/methods/constructors are discovered and invoked using reflection
* </ul>
* The scenarios after that then how how to plug in extensions:<br>
* <ul>
* <li>Adding entries to the classpath that will be used to load types and define well known 'imports'
* <li>Defining variables that are then accessible in the expression
* <li>Changing the root context object against which non-qualified references are resolved
* <li>Registering java methods as functions callable from the expression
* <li>Adding a basic property resolver
* <li>Adding an advanced (better performing) property resolver
* <li>Adding your own type converter to support conversion between any types you like
* </ul>
*
* @author Andy Clement
*/
public class ExpressionLanguageScenarioTests extends ExpressionTestCase {
/**
* Scenario: using the standard infrastructure and running simple expression evaluation.
*/
@Test
public void testScenario_UsingStandardInfrastructure() {
try {
// Create a parser
SpelExpressionParser parser = new SpelExpressionParser();
// Parse an expression
Expression expr = parser.parseRaw("new String('hello world')");
// Evaluate it using a 'standard' context
Object value = expr.getValue();
// They are reusable
value = expr.getValue();
Assert.assertEquals("hello world", value);
Assert.assertEquals(String.class, value.getClass());
} catch (EvaluationException ee) {
ee.printStackTrace();
Assert.fail("Unexpected Exception: " + ee.getMessage());
} catch (ParseException pe) {
pe.printStackTrace();
Assert.fail("Unexpected Exception: " + pe.getMessage());
}
}
/**
* Scenario: using the standard context but adding your own variables
*/
@Test
public void testScenario_DefiningVariablesThatWillBeAccessibleInExpressions() throws Exception {
// Create a parser
SpelExpressionParser parser = new SpelExpressionParser();
// Use the standard evaluation context
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.setVariable("favouriteColour","blue");
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
ctx.setVariable("primes",primes);
Expression expr = parser.parseRaw("#favouriteColour");
Object value = expr.getValue(ctx);
Assert.assertEquals("blue", value);
expr = parser.parseRaw("#primes.get(1)");
value = expr.getValue(ctx);
Assert.assertEquals(3, value);
// all prime numbers > 10 from the list (using selection ?{...})
expr = parser.parseRaw("#primes.?[#this>10]");
value = expr.getValue(ctx);
Assert.assertEquals("[11, 13, 17]", value.toString());
}
static class TestClass {
public String str;
private int property;
public int getProperty() { return property; }
public void setProperty(int i) { property = i; }
}
/**
* Scenario: using your own root context object
*/
@Test
public void testScenario_UsingADifferentRootContextObject() throws Exception {
// Create a parser
SpelExpressionParser parser = new SpelExpressionParser();
// Use the standard evaluation context
StandardEvaluationContext ctx = new StandardEvaluationContext();
TestClass tc = new TestClass();
tc.setProperty(42);
tc.str = "wibble";
ctx.setRootObject(tc);
// read it, set it, read it again
Expression expr = parser.parseRaw("str");
Object value = expr.getValue(ctx);
Assert.assertEquals("wibble", value);
expr = parser.parseRaw("str");
expr.setValue(ctx, "wobble");
expr = parser.parseRaw("str");
value = expr.getValue(ctx);
Assert.assertEquals("wobble", value);
// or using assignment within the expression
expr = parser.parseRaw("str='wabble'");
value = expr.getValue(ctx);
expr = parser.parseRaw("str");
value = expr.getValue(ctx);
Assert.assertEquals("wabble", value);
// private property will be accessed through getter()
expr = parser.parseRaw("property");
value = expr.getValue(ctx);
Assert.assertEquals(42, value);
// ... and set through setter
expr = parser.parseRaw("property=4");
value = expr.getValue(ctx);
expr = parser.parseRaw("property");
value = expr.getValue(ctx);
Assert.assertEquals(4,value);
}
public static String repeat(String s) { return s+s; }
/**
* Scenario: using your own java methods and calling them from the expression
*/
@Test
public void testScenario_RegisteringJavaMethodsAsFunctionsAndCallingThem() throws SecurityException, NoSuchMethodException {
try {
// Create a parser
SpelExpressionParser parser = new SpelExpressionParser();
// Use the standard evaluation context
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.registerFunction("repeat",ExpressionLanguageScenarioTests.class.getDeclaredMethod("repeat",String.class));
Expression expr = parser.parseRaw("#repeat('hello')");
Object value = expr.getValue(ctx);
Assert.assertEquals("hellohello", value);
} catch (EvaluationException ee) {
ee.printStackTrace();
Assert.fail("Unexpected Exception: " + ee.getMessage());
} catch (ParseException pe) {
pe.printStackTrace();
Assert.fail("Unexpected Exception: " + pe.getMessage());
}
}
/**
* Scenario: add a property resolver that will get called in the resolver chain, this one only supports reading.
*/
@Test
public void testScenario_AddingYourOwnPropertyResolvers_1() throws Exception {
// Create a parser
SpelExpressionParser parser = new SpelExpressionParser();
// Use the standard evaluation context
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.addPropertyAccessor(new FruitColourAccessor());
Expression expr = parser.parseRaw("orange");
Object value = expr.getValue(ctx);
Assert.assertEquals(Color.orange, value);
try {
expr.setValue(ctx, Color.blue);
Assert.fail("Should not be allowed to set oranges to be blue !");
} catch (SpelEvaluationException ee) {
Assert.assertEquals(ee.getMessageCode(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL);
}
}
@Test
public void testScenario_AddingYourOwnPropertyResolvers_2() throws Exception {
// Create a parser
SpelExpressionParser parser = new SpelExpressionParser();
// Use the standard evaluation context
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.addPropertyAccessor(new VegetableColourAccessor());
Expression expr = parser.parseRaw("pea");
Object value = expr.getValue(ctx);
Assert.assertEquals(Color.green, value);
try {
expr.setValue(ctx, Color.blue);
Assert.fail("Should not be allowed to set peas to be blue !");
}
catch (SpelEvaluationException ee) {
Assert.assertEquals(ee.getMessageCode(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL);
}
}
/**
* Regardless of the current context object, or root context object, this resolver can tell you what colour a fruit is !
* It only supports property reading, not writing. To support writing it would need to override canWrite() and write()
*/
private static class FruitColourAccessor implements PropertyAccessor {
private static Map<String,Color> propertyMap = new HashMap<String,Color>();
static {
propertyMap.put("banana",Color.yellow);
propertyMap.put("apple",Color.red);
propertyMap.put("orange",Color.orange);
}
/**
* Null means you might be able to read any property, if an earlier property resolver hasn't beaten you to it
*/
public Class<?>[] getSpecificTargetClasses() {
return null;
}
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
return propertyMap.containsKey(name);
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(propertyMap.get(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 {
}
}
/**
* Regardless of the current context object, or root context object, this resolver can tell you what colour a vegetable is !
* It only supports property reading, not writing.
*/
private static class VegetableColourAccessor implements PropertyAccessor {
private static Map<String,Color> propertyMap = new HashMap<String,Color>();
static {
propertyMap.put("carrot",Color.orange);
propertyMap.put("pea",Color.green);
}
/**
* Null means you might be able to read any property, if an earlier property resolver hasn't beaten you to it
*/
public Class<?>[] getSpecificTargetClasses() {
return null;
}
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
return propertyMap.containsKey(name);
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(propertyMap.get(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 {
}
}
}

View File

@@ -0,0 +1,286 @@
/*
* Copyright 2002-2009 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.util.EmptyStackException;
import java.util.HashMap;
import java.util.Map;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.testresources.Inventor;
/**
* Tests for the expression state object - some features are not yet exploited in the language (eg nested scopes)
*
* @author Andy Clement
*/
public class ExpressionStateTests extends ExpressionTestCase {
@Test
public void testConstruction() {
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
ExpressionState state = new ExpressionState(context);
Assert.assertEquals(context,state.getEvaluationContext());
}
// Local variables are in variable scopes which come and go during evaluation. Normal variables are
// accessible through the evaluation context
@Test
public void testLocalVariables() {
ExpressionState state = getState();
Object value = state.lookupLocalVariable("foo");
Assert.assertNull(value);
state.setLocalVariable("foo",34);
value = state.lookupLocalVariable("foo");
Assert.assertEquals(34,value);
state.setLocalVariable("foo",null);
value = state.lookupLocalVariable("foo");
Assert.assertEquals(null,value);
}
@Test
public void testVariables() {
ExpressionState state = getState();
TypedValue typedValue = state.lookupVariable("foo");
Assert.assertEquals(TypedValue.NULL,typedValue);
state.setVariable("foo",34);
typedValue = state.lookupVariable("foo");
Assert.assertEquals(34,typedValue.getValue());
Assert.assertEquals(Integer.class,typedValue.getTypeDescriptor().getType());
state.setVariable("foo","abc");
typedValue = state.lookupVariable("foo");
Assert.assertEquals("abc",typedValue.getValue());
Assert.assertEquals(String.class,typedValue.getTypeDescriptor().getType());
}
@Test
public void testNoVariableInteference() {
ExpressionState state = getState();
TypedValue typedValue = state.lookupVariable("foo");
Assert.assertEquals(TypedValue.NULL,typedValue);
state.setLocalVariable("foo",34);
typedValue = state.lookupVariable("foo");
Assert.assertEquals(TypedValue.NULL,typedValue);
state.setVariable("goo","hello");
Assert.assertNull(state.lookupLocalVariable("goo"));
}
@Test
public void testLocalVariableNestedScopes() {
ExpressionState state = getState();
Assert.assertEquals(null,state.lookupLocalVariable("foo"));
state.setLocalVariable("foo",12);
Assert.assertEquals(12,state.lookupLocalVariable("foo"));
state.enterScope(null);
Assert.assertEquals(12,state.lookupLocalVariable("foo")); // found in upper scope
state.setLocalVariable("foo","abc");
Assert.assertEquals("abc",state.lookupLocalVariable("foo")); // found in nested scope
state.exitScope();
Assert.assertEquals(12,state.lookupLocalVariable("foo")); // found in nested scope
}
@Test
public void testRootContextObject() {
ExpressionState state = getState();
Assert.assertEquals(Inventor.class,state.getRootContextObject().getValue().getClass());
// although the root object is being set on the evaluation context, the value in the 'state' remains what it was when constructed
((StandardEvaluationContext) state.getEvaluationContext()).setRootObject(null);
Assert.assertEquals(Inventor.class,state.getRootContextObject().getValue().getClass());
// Assert.assertEquals(null, state.getRootContextObject().getValue());
state = new ExpressionState(new StandardEvaluationContext());
Assert.assertEquals(TypedValue.NULL,state.getRootContextObject());
((StandardEvaluationContext)state.getEvaluationContext()).setRootObject(null);
Assert.assertEquals(null,state.getRootContextObject().getValue());
}
@Test
public void testActiveContextObject() {
ExpressionState state = getState();
Assert.assertEquals(state.getRootContextObject().getValue(),state.getActiveContextObject().getValue());
try {
state.popActiveContextObject();
Assert.fail("stack should be empty...");
} catch (EmptyStackException ese) {
// success
}
state.pushActiveContextObject(new TypedValue(34));
Assert.assertEquals(34,state.getActiveContextObject().getValue());
state.pushActiveContextObject(new TypedValue("hello"));
Assert.assertEquals("hello",state.getActiveContextObject().getValue());
state.popActiveContextObject();
Assert.assertEquals(34,state.getActiveContextObject().getValue());
state.popActiveContextObject();
Assert.assertEquals(state.getRootContextObject().getValue(),state.getActiveContextObject().getValue());
state = new ExpressionState(new StandardEvaluationContext());
Assert.assertEquals(TypedValue.NULL,state.getActiveContextObject());
}
@Test
public void testPopulatedNestedScopes() {
ExpressionState state = getState();
Assert.assertNull(state.lookupLocalVariable("foo"));
state.enterScope("foo",34);
Assert.assertEquals(34,state.lookupLocalVariable("foo"));
state.enterScope(null);
state.setLocalVariable("foo",12);
Assert.assertEquals(12,state.lookupLocalVariable("foo"));
state.exitScope();
Assert.assertEquals(34,state.lookupLocalVariable("foo"));
state.exitScope();
Assert.assertNull(state.lookupLocalVariable("goo"));
}
@Test
public void testRootObjectConstructor() {
EvaluationContext ctx = getContext();
// TypedValue root = ctx.getRootObject();
// supplied should override root on context
ExpressionState state = new ExpressionState(ctx,new TypedValue("i am a string"));
TypedValue stateRoot = state.getRootContextObject();
Assert.assertEquals(String.class,stateRoot.getTypeDescriptor().getType());
Assert.assertEquals("i am a string",stateRoot.getValue());
}
@Test
public void testPopulatedNestedScopesMap() {
ExpressionState state = getState();
Assert.assertNull(state.lookupLocalVariable("foo"));
Assert.assertNull(state.lookupLocalVariable("goo"));
Map<String,Object> m = new HashMap<String,Object>();
m.put("foo",34);
m.put("goo","abc");
state.enterScope(m);
Assert.assertEquals(34,state.lookupLocalVariable("foo"));
Assert.assertEquals("abc",state.lookupLocalVariable("goo"));
state.enterScope(null);
state.setLocalVariable("foo",12);
Assert.assertEquals(12,state.lookupLocalVariable("foo"));
Assert.assertEquals("abc",state.lookupLocalVariable("goo"));
state.exitScope();
state.exitScope();
Assert.assertNull(state.lookupLocalVariable("foo"));
Assert.assertNull(state.lookupLocalVariable("goo"));
}
@Test
public void testOperators() throws Exception {
ExpressionState state = getState();
try {
state.operate(Operation.ADD,1,2);
Assert.fail("should have failed");
} catch (EvaluationException ee) {
SpelEvaluationException sEx = (SpelEvaluationException)ee;
Assert.assertEquals(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES,sEx.getMessageCode());
}
try {
state.operate(Operation.ADD,null,null);
Assert.fail("should have failed");
} catch (EvaluationException ee) {
SpelEvaluationException sEx = (SpelEvaluationException)ee;
Assert.assertEquals(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES,sEx.getMessageCode());
}
}
@Test
public void testComparator() {
ExpressionState state = getState();
Assert.assertEquals(state.getEvaluationContext().getTypeComparator(),state.getTypeComparator());
}
@Test
public void testTypeLocator() throws EvaluationException {
ExpressionState state = getState();
Assert.assertNotNull(state.getEvaluationContext().getTypeLocator());
Assert.assertEquals(Integer.class,state.findType("java.lang.Integer"));
try {
state.findType("someMadeUpName");
Assert.fail("Should have failed to find it");
} catch (EvaluationException ee) {
SpelEvaluationException sEx = (SpelEvaluationException)ee;
Assert.assertEquals(SpelMessage.TYPE_NOT_FOUND,sEx.getMessageCode());
}
}
@Test
public void testTypeConversion() throws EvaluationException {
ExpressionState state = getState();
String s = (String)state.convertValue(34, TypeDescriptor.valueOf(String.class));
Assert.assertEquals("34",s);
s = (String)state.convertValue(new TypedValue(34), TypeDescriptor.valueOf(String.class));
Assert.assertEquals("34",s);
}
@Test
public void testPropertyAccessors() {
ExpressionState state = getState();
Assert.assertEquals(state.getEvaluationContext().getPropertyAccessors(),state.getPropertyAccessors());
}
/**
* @return a new ExpressionState
*/
private ExpressionState getState() {
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
ExpressionState state = new ExpressionState(context);
return state;
}
private EvaluationContext getContext() {
return TestScenarioCreator.getTestEvaluationContext();
}
}

View File

@@ -0,0 +1,421 @@
/*
* Copyright 2002-2009 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.util.Arrays;
import java.util.List;
import junit.framework.Assert;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* Common superclass for expression tests.
*
* @author Andy Clement
*/
public abstract class ExpressionTestCase {
private final static boolean DEBUG = false;
protected final static boolean SHOULD_BE_WRITABLE = true;
protected final static boolean SHOULD_NOT_BE_WRITABLE = false;
protected final static ExpressionParser parser = new SpelExpressionParser();
protected final static StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
/**
* Evaluate an expression and check that the actual result matches the expectedValue and the class of the result
* matches the expectedClassOfResult.
* @param expression The expression to evaluate
* @param expectedValue the expected result for evaluating the expression
* @param expectedResultType the expected class of the evaluation result
*/
public void evaluate(String expression, Object expectedValue, Class<?> expectedResultType) {
try {
Expression expr = parser.parseExpression(expression);
if (expr == null) {
Assert.fail("Parser returned null for expression");
}
if (DEBUG) {
SpelUtilities.printAbstractSyntaxTree(System.out, expr);
}
// Class<?> expressionType = expr.getValueType();
// Assert.assertEquals("Type of the expression is not as expected. Should be '"+expectedResultType+"' but is
// '"+expressionType+"'",
// expectedResultType,expressionType);
Object value = expr.getValue(eContext);
// Check the return value
if (value == null) {
if (expectedValue == null) {
return; // no point doing other checks
}
Assert.assertEquals("Expression returned null value, but expected '" + expectedValue + "'", expectedValue,
null);
}
Class<?> resultType = value.getClass();
Assert.assertEquals("Type of the actual result was not as expected. Expected '" + expectedResultType
+ "' but result was of type '" + resultType + "'", expectedResultType, resultType);
// .equals/* isAssignableFrom */(resultType), truers);
// TODO isAssignableFrom would allow some room for compatibility
// in the above expression...
if (expectedValue instanceof String) {
Assert.assertEquals("Did not get expected value for expression '" + expression + "'.", expectedValue,
ExpressionTestCase.stringValueOf(value));
} else {
Assert.assertEquals("Did not get expected value for expression '" + expression + "'.", expectedValue, value);
}
} catch (EvaluationException ee) {
ee.printStackTrace();
Assert.fail("Unexpected Exception: " + ee.getMessage());
} catch (ParseException pe) {
pe.printStackTrace();
Assert.fail("Unexpected Exception: " + pe.getMessage());
}
}
public void evaluateAndAskForReturnType(String expression, Object expectedValue, Class<?> expectedResultType) {
try {
Expression expr = parser.parseExpression(expression);
if (expr == null) {
Assert.fail("Parser returned null for expression");
}
if (DEBUG) {
SpelUtilities.printAbstractSyntaxTree(System.out, expr);
}
// Class<?> expressionType = expr.getValueType();
// Assert.assertEquals("Type of the expression is not as expected. Should be '"+expectedResultType+"' but is
// '"+expressionType+"'",
// expectedResultType,expressionType);
Object value = expr.getValue(eContext, expectedResultType);
if (value == null) {
if (expectedValue == null)
return; // no point doing other checks
Assert.assertEquals("Expression returned null value, but expected '" + expectedValue + "'", expectedValue,
null);
}
Class<?> resultType = value.getClass();
Assert.assertEquals("Type of the actual result was not as expected. Expected '" + expectedResultType
+ "' but result was of type '" + resultType + "'", expectedResultType, resultType);
// .equals/* isAssignableFrom */(resultType), truers);
Assert.assertEquals("Did not get expected value for expression '" + expression + "'.", expectedValue, value);
// isAssignableFrom would allow some room for compatibility
// in the above expression...
} catch (EvaluationException ee) {
SpelEvaluationException ex = (SpelEvaluationException) ee;
ex.printStackTrace();
Assert.fail("Unexpected EvaluationException: " + ex.getMessage());
} catch (ParseException pe) {
Assert.fail("Unexpected ParseException: " + pe.getMessage());
}
}
/**
* Evaluate an expression and check that the actual result matches the expectedValue and the class of the result
* matches the expectedClassOfResult. This method can also check if the expression is writable (for example, it is a
* variable or property reference).
*
* @param expression The expression to evaluate
* @param expectedValue the expected result for evaluating the expression
* @param expectedClassOfResult the expected class of the evaluation result
* @param shouldBeWritable should the parsed expression be writable?
*/
public void evaluate(String expression, Object expectedValue, Class<?> expectedClassOfResult,
boolean shouldBeWritable) {
try {
Expression e = parser.parseExpression(expression);
if (e == null) {
Assert.fail("Parser returned null for expression");
}
if (DEBUG) {
SpelUtilities.printAbstractSyntaxTree(System.out, e);
}
Object value = e.getValue(eContext);
if (value == null) {
if (expectedValue == null)
return; // no point doing other
// checks
Assert.assertEquals("Expression returned null value, but expected '" + expectedValue + "'", expectedValue,
null);
}
Class<? extends Object> resultType = value.getClass();
if (expectedValue instanceof String) {
Assert.assertEquals("Did not get expected value for expression '" + expression + "'.", expectedValue,
ExpressionTestCase.stringValueOf(value));
} else {
Assert.assertEquals("Did not get expected value for expression '" + expression + "'.", expectedValue, value);
}
// Assert.assertEquals("Did not get expected value for expression '" + expression + "'.", expectedValue,
// ExpressionTestCase.stringValueOf(value));
Assert.assertEquals("Type of the result was not as expected. Expected '" + expectedClassOfResult
+ "' but result was of type '" + resultType + "'", expectedClassOfResult
.equals/* isAssignableFrom */(resultType), true);
// TODO isAssignableFrom would allow some room for compatibility
// in the above expression...
boolean isWritable = e.isWritable(eContext);
if (isWritable != shouldBeWritable) {
if (shouldBeWritable)
Assert.fail("Expected the expression to be writable but it is not");
else
Assert.fail("Expected the expression to be readonly but it is not");
}
} catch (EvaluationException ee) {
ee.printStackTrace();
Assert.fail("Unexpected Exception: " + ee.getMessage());
} catch (ParseException pe) {
pe.printStackTrace();
Assert.fail("Unexpected Exception: " + pe.getMessage());
}
}
/**
* Evaluate the specified expression and ensure the expected message comes out. The message may have inserts and
* they will be checked if otherProperties is specified. The first entry in otherProperties should always be the
* position.
* @param expression The expression to evaluate
* @param expectedMessage The expected message
* @param otherProperties The expected inserts within the message
*/
protected void evaluateAndCheckError(String expression, SpelMessage expectedMessage, Object... otherProperties) {
evaluateAndCheckError(expression, null, expectedMessage, otherProperties);
}
/**
* Evaluate the specified expression and ensure the expected message comes out. The message may have inserts and
* they will be checked if otherProperties is specified. The first entry in otherProperties should always be the
* position.
* @param expression The expression to evaluate
* @param expectedReturnType Ask the expression return value to be of this type if possible (null indicates don't
* ask for conversion)
* @param expectedMessage The expected message
* @param otherProperties The expected inserts within the message
*/
protected void evaluateAndCheckError(String expression, Class<?> expectedReturnType, SpelMessage expectedMessage,
Object... otherProperties) {
try {
Expression expr = parser.parseExpression(expression);
if (expr == null) {
Assert.fail("Parser returned null for expression");
}
if (expectedReturnType != null) {
@SuppressWarnings("unused")
Object value = expr.getValue(eContext, expectedReturnType);
} else {
@SuppressWarnings("unused")
Object value = expr.getValue(eContext);
}
Assert.fail("Should have failed with message " + expectedMessage);
} catch (EvaluationException ee) {
SpelEvaluationException ex = (SpelEvaluationException) ee;
if (ex.getMessageCode() != expectedMessage) {
// System.out.println(ex.getMessage());
ex.printStackTrace();
Assert.assertEquals("Failed to get expected message", expectedMessage, ex.getMessageCode());
}
if (otherProperties != null && otherProperties.length != 0) {
// first one is expected position of the error within the string
int pos = ((Integer) otherProperties[0]).intValue();
Assert.assertEquals("Did not get correct position reported in error ", pos, ex.getPosition());
if (otherProperties.length > 1) {
// Check inserts match
Object[] inserts = ex.getInserts();
if (inserts == null) {
inserts = new Object[0];
}
if (inserts.length < otherProperties.length - 1) {
ex.printStackTrace();
Assert.fail("Cannot check " + (otherProperties.length - 1)
+ " properties of the exception, it only has " + inserts.length + " inserts");
}
for (int i = 1; i < otherProperties.length; i++) {
if (otherProperties[i] == null) {
if (inserts[i - 1] != null) {
ex.printStackTrace();
Assert.fail("Insert does not match, expected 'null' but insert value was '" + inserts[i - 1]
+ "'");
}
} else if (inserts[i - 1] == null) {
if (otherProperties[i] != null) {
ex.printStackTrace();
Assert.fail("Insert does not match, expected '" + otherProperties[i]
+ "' but insert value was 'null'");
}
} else if (!inserts[i - 1].equals(otherProperties[i])) {
ex.printStackTrace();
Assert.fail("Insert does not match, expected '" + otherProperties[i] + "' but insert value was '"
+ inserts[i - 1] + "'");
}
}
}
}
} catch (ParseException pe) {
pe.printStackTrace();
Assert.fail("Unexpected Exception: " + pe.getMessage());
}
}
/**
* Parse the specified expression and ensure the expected message comes out. The message may have inserts and they
* will be checked if otherProperties is specified. The first entry in otherProperties should always be the
* position.
* @param expression The expression to evaluate
* @param expectedMessage The expected message
* @param otherProperties The expected inserts within the message
*/
protected void parseAndCheckError(String expression, SpelMessage expectedMessage, Object... otherProperties) {
try {
Expression expr = parser.parseExpression(expression);
SpelUtilities.printAbstractSyntaxTree(System.out, expr);
Assert.fail("Parsing should have failed!");
} catch (ParseException pe) {
// pe.printStackTrace();
// Throwable t = pe.getCause();
// if (t == null) {
// Assert.fail("ParseException caught with no defined cause");
// }
// if (!(t instanceof SpelEvaluationException)) {
// t.printStackTrace();
// Assert.fail("Cause of parse exception is not a SpelException");
// }
// SpelEvaluationException ex = (SpelEvaluationException) t;
// pe.printStackTrace();
SpelParseException ex = (SpelParseException)pe;
if (ex.getMessageCode() != expectedMessage) {
// System.out.println(ex.getMessage());
ex.printStackTrace();
Assert.assertEquals("Failed to get expected message", expectedMessage, ex.getMessageCode());
}
if (otherProperties != null && otherProperties.length != 0) {
// first one is expected position of the error within the string
int pos = ((Integer) otherProperties[0]).intValue();
Assert.assertEquals("Did not get correct position reported in error ", pos, ex.getPosition());
if (otherProperties.length > 1) {
// Check inserts match
Object[] inserts = ex.getInserts();
if (inserts == null) {
inserts = new Object[0];
}
if (inserts.length < otherProperties.length - 1) {
ex.printStackTrace();
Assert.fail("Cannot check " + (otherProperties.length - 1)
+ " properties of the exception, it only has " + inserts.length + " inserts");
}
for (int i = 1; i < otherProperties.length; i++) {
if (!inserts[i - 1].equals(otherProperties[i])) {
ex.printStackTrace();
Assert.fail("Insert does not match, expected '" + otherProperties[i] + "' but insert value was '"
+ inserts[i - 1] + "'");
}
}
}
}
}
}
public static String stringValueOf(Object value) {
return stringValueOf(value, false);
}
/**
* Produce a nice string representation of the input object.
*
* @param value object to be formatted
* @return a nice string
*/
public static String stringValueOf(Object value, boolean isNested) {
// do something nice for arrays
if (value == null) {
return "null";
}
if (value.getClass().isArray()) {
StringBuilder sb = new StringBuilder();
if (value.getClass().getComponentType().isPrimitive()) {
Class<?> primitiveType = value.getClass().getComponentType();
if (primitiveType == Integer.TYPE) {
int[] l = (int[]) value;
sb.append("int[").append(l.length).append("]{");
for (int j = 0; j < l.length; j++) {
if (j > 0) {
sb.append(",");
}
sb.append(stringValueOf(l[j]));
}
sb.append("}");
} else if (primitiveType == Long.TYPE) {
long[] l = (long[]) value;
sb.append("long[").append(l.length).append("]{");
for (int j = 0; j < l.length; j++) {
if (j > 0) {
sb.append(",");
}
sb.append(stringValueOf(l[j]));
}
sb.append("}");
} else {
throw new RuntimeException("Please implement support for type " + primitiveType.getName()
+ " in ExpressionTestCase.stringValueOf()");
}
} else if (value.getClass().getComponentType().isArray()) {
List<Object> l = Arrays.asList((Object[]) value);
if (!isNested) {
sb.append(value.getClass().getComponentType().getName());
}
sb.append("[").append(l.size()).append("]{");
int i = 0;
for (Object object : l) {
if (i > 0) {
sb.append(",");
}
i++;
sb.append(stringValueOf(object, true));
}
sb.append("}");
} else {
List<Object> l = Arrays.asList((Object[]) value);
if (!isNested) {
sb.append(value.getClass().getComponentType().getName());
}
sb.append("[").append(l.size()).append("]{");
int i = 0;
for (Object object : l) {
if (i > 0) {
sb.append(",");
}
i++;
sb.append(stringValueOf(object));
}
sb.append("}");
}
return sb.toString();
} else {
return value.toString();
}
}
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2002-2011 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 static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* Expression evaluation where the TypeConverter plugged in is the
* {@link org.springframework.core.convert.support.GenericConversionService}.
*
* @author Andy Clement
* @author Dave Syer
*/
public class ExpressionTestsUsingCoreConversionService extends ExpressionTestCase {
private static List<String> listOfString = new ArrayList<String>();
private static TypeDescriptor typeDescriptorForListOfString = null;
private static List<Integer> listOfInteger = new ArrayList<Integer>();
private static TypeDescriptor typeDescriptorForListOfInteger = null;
static {
listOfString.add("1");
listOfString.add("2");
listOfString.add("3");
listOfInteger.add(4);
listOfInteger.add(5);
listOfInteger.add(6);
}
@Before
public void setUp() throws Exception {
ExpressionTestsUsingCoreConversionService.typeDescriptorForListOfString = new TypeDescriptor(ExpressionTestsUsingCoreConversionService.class.getDeclaredField("listOfString"));
ExpressionTestsUsingCoreConversionService.typeDescriptorForListOfInteger = new TypeDescriptor(ExpressionTestsUsingCoreConversionService.class.getDeclaredField("listOfInteger"));
}
/**
* Test the service can convert what we are about to use in the expression evaluation tests.
*/
@Test
public void testConversionsAvailable() throws Exception {
TypeConvertorUsingConversionService tcs = new TypeConvertorUsingConversionService();
// ArrayList containing List<Integer> to List<String>
Class<?> clazz = typeDescriptorForListOfString.getElementTypeDescriptor().getType();
assertEquals(String.class,clazz);
List l = (List) tcs.convertValue(listOfInteger, TypeDescriptor.forObject(listOfInteger), typeDescriptorForListOfString);
assertNotNull(l);
// ArrayList containing List<String> to List<Integer>
clazz = typeDescriptorForListOfInteger.getElementTypeDescriptor().getType();
assertEquals(Integer.class,clazz);
l = (List) tcs.convertValue(listOfString, TypeDescriptor.forObject(listOfString), typeDescriptorForListOfString);
assertNotNull(l);
}
@Test
public void testSetParameterizedList() throws Exception {
StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
Expression e = parser.parseExpression("listOfInteger.size()");
assertEquals(0,e.getValue(context,Integer.class).intValue());
context.setTypeConverter(new TypeConvertorUsingConversionService());
// Assign a List<String> to the List<Integer> field - the component elements should be converted
parser.parseExpression("listOfInteger").setValue(context,listOfString);
assertEquals(3,e.getValue(context,Integer.class).intValue()); // size now 3
Class clazz = parser.parseExpression("listOfInteger[1].getClass()").getValue(context,Class.class); // element type correctly Integer
assertEquals(Integer.class,clazz);
}
@Test
public void testCoercionToCollectionOfPrimitive() throws Exception {
class TestTarget {
@SuppressWarnings("unused")
public int sum(Collection<Integer> numbers) {
int total = 0;
for (int i : numbers) {
total += i;
}
return total;
}
}
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
TypeDescriptor collectionType = new TypeDescriptor(new MethodParameter(TestTarget.class.getDeclaredMethod(
"sum", Collection.class), 0));
// The type conversion is possible
assertTrue(evaluationContext.getTypeConverter()
.canConvert(TypeDescriptor.valueOf(String.class), collectionType));
// ... and it can be done successfully
assertEquals("[1, 2, 3, 4]", evaluationContext.getTypeConverter().convertValue("1,2,3,4", TypeDescriptor.valueOf(String.class), collectionType).toString());
evaluationContext.setVariable("target", new TestTarget());
// OK up to here, so the evaluation should be fine...
// ... but this fails
int result = (Integer) parser.parseExpression("#target.sum(#root)").getValue(evaluationContext, "1,2,3,4");
assertEquals("Wrong result: " + result, 10, result);
}
public static class Foo {
private Collection<Foo> foos;
public final String value;
public Foo(String value) {
this.value = value;
}
public void setFoos(Collection<Foo> foos) {
this.foos = foos;
}
public Collection<Foo> getFoos() {
return this.foos;
}
}
@Test
public void testConvert() {
Foo root = new Foo("bar");
StandardEvaluationContext context = new StandardEvaluationContext(root);
Collection<String> foos = Collections.singletonList("baz");
// property access, works
Expression expression = parser.parseExpression("foos");
expression.setValue(context, foos);
Foo baz = root.getFoos().iterator().next();
assertEquals("baz", baz.value);
// method call, fails (ClassCastException)
expression = parser.parseExpression("setFoos(#foos)");
context.setVariable("foos", foos);
expression.getValue(context);
baz = root.getFoos().iterator().next();
assertEquals("baz", baz.value);
}
/**
* Type converter that uses the core conversion service.
*/
private static class TypeConvertorUsingConversionService implements TypeConverter {
private final ConversionService service = new DefaultConversionService();
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.service.canConvert(sourceType, targetType);
}
public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) throws EvaluationException {
return this.service.convert(value, sourceType, targetType);
}
}
}

View File

@@ -0,0 +1,426 @@
/*
* Copyright 2002-2009 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.util.ArrayList;
import java.util.HashMap;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* These are tests for language features that are not yet considered 'live'. Either missing implementation or
* documentation.
*
* Where implementation is missing the tests are commented out.
*
* @author Andy Clement
*/
public class InProgressTests extends ExpressionTestCase {
@Test
public void testRelOperatorsBetween01() {
evaluate("1 between listOneFive", "true", Boolean.class);
// evaluate("1 between {1, 5}", "true", Boolean.class); // no inline list building at the moment
}
@Test
public void testRelOperatorsBetweenErrors01() {
evaluateAndCheckError("1 between T(String)", SpelMessage.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST, 10);
}
@Test
public void testRelOperatorsBetweenErrors03() {
evaluateAndCheckError("1 between listOfNumbersUpToTen",
SpelMessage.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST, 10);
}
// PROJECTION
@Test
public void testProjection01() {
evaluate("listOfNumbersUpToTen.![#this<5?'y':'n']", "[y, y, y, y, n, n, n, n, n, n]", ArrayList.class);
// inline list creation not supported at the moment
// evaluate("{1,2,3,4,5,6,7,8,9,10}.!{#isEven(#this)}", "[n, y, n, y, n, y, n, y, n, y]", ArrayList.class);
}
@Test
public void testProjection02() {
// inline map creation not supported at the moment
// evaluate("#{'a':'y','b':'n','c':'y'}.![value=='y'?key:null].nonnull().sort()", "[a, c]", ArrayList.class);
evaluate("mapOfNumbersUpToTen.![key>5?value:null]",
"[null, null, null, null, null, six, seven, eight, nine, ten]", ArrayList.class);
}
@Test
public void testProjection05() {
evaluateAndCheckError("'abc'.![true]", SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE);
evaluateAndCheckError("null.![true]", SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE);
evaluate("null?.![true]", null, null);
}
@Test
public void testProjection06() throws Exception {
SpelExpression expr = (SpelExpression) parser.parseExpression("'abc'.![true]");
Assert.assertEquals("'abc'.![true]", expr.toStringAST());
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
}
// SELECTION
@Test
public void testSelection02() {
evaluate("testMap.keySet().?[#this matches '.*o.*']", "[monday]", ArrayList.class);
evaluate("testMap.keySet().?[#this matches '.*r.*'].contains('saturday')", "true", Boolean.class);
evaluate("testMap.keySet().?[#this matches '.*r.*'].size()", "3", Integer.class);
}
@Test
public void testSelectionError_NonBooleanSelectionCriteria() {
evaluateAndCheckError("listOfNumbersUpToTen.?['nonboolean']",
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);
}
@Test
public void testSelection03() {
evaluate("mapOfNumbersUpToTen.?[key>5].size()", "5", Integer.class);
// evaluate("listOfNumbersUpToTen.?{#this>5}", "5", ArrayList.class);
}
@Test
public void testSelection04() {
evaluateAndCheckError("mapOfNumbersUpToTen.?['hello'].size()",
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);
}
@Test
public void testSelection05() {
evaluate("mapOfNumbersUpToTen.?[key>11].size()", "0", Integer.class);
evaluate("mapOfNumbersUpToTen.^[key>11]", null, null);
evaluate("mapOfNumbersUpToTen.$[key>11]", null, null);
evaluate("null?.$[key>11]", null, null);
evaluateAndCheckError("null.?[key>11]", SpelMessage.INVALID_TYPE_FOR_SELECTION);
evaluateAndCheckError("'abc'.?[key>11]", SpelMessage.INVALID_TYPE_FOR_SELECTION);
}
@Test
public void testSelectionFirst01() {
evaluate("listOfNumbersUpToTen.^[#isEven(#this) == 'y']", "2", Integer.class);
}
@Test
public void testSelectionFirst02() {
evaluate("mapOfNumbersUpToTen.^[key>5].size()", "1", Integer.class);
}
@Test
public void testSelectionLast01() {
evaluate("listOfNumbersUpToTen.$[#isEven(#this) == 'y']", "10", Integer.class);
}
@Test
public void testSelectionLast02() {
evaluate("mapOfNumbersUpToTen.$[key>5]", "{10=ten}", HashMap.class);
evaluate("mapOfNumbersUpToTen.$[key>5].size()", "1", Integer.class);
}
@Test
public void testSelectionAST() throws Exception {
SpelExpression expr = (SpelExpression) parser.parseExpression("'abc'.^[true]");
Assert.assertEquals("'abc'.^[true]", expr.toStringAST());
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
expr = (SpelExpression) parser.parseExpression("'abc'.?[true]");
Assert.assertEquals("'abc'.?[true]", expr.toStringAST());
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
expr = (SpelExpression) parser.parseExpression("'abc'.$[true]");
Assert.assertEquals("'abc'.$[true]", expr.toStringAST());
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
}
// Constructor invocation
// public void testPrimitiveTypeArrayConstructors() {
// evaluate("new int[]{1,2,3,4}.count()", 4, Integer.class);
// evaluate("new boolean[]{true,false,true}.count()", 3, Integer.class);
// evaluate("new char[]{'a','b','c'}.count()", 3, Integer.class);
// evaluate("new long[]{1,2,3,4,5}.count()", 5, Integer.class);
// evaluate("new short[]{2,3,4,5,6}.count()", 5, Integer.class);
// evaluate("new double[]{1d,2d,3d,4d}.count()", 4, Integer.class);
// evaluate("new float[]{1f,2f,3f,4f}.count()", 4, Integer.class);
// evaluate("new byte[]{1,2,3,4}.count()", 4, Integer.class);
// }
//
// public void testPrimitiveTypeArrayConstructorsElements() {
// evaluate("new int[]{1,2,3,4}[0]", 1, Integer.class);
// evaluate("new boolean[]{true,false,true}[0]", true, Boolean.class);
// evaluate("new char[]{'a','b','c'}[0]", 'a', Character.class);
// evaluate("new long[]{1,2,3,4,5}[0]", 1L, Long.class);
// evaluate("new short[]{2,3,4,5,6}[0]", (short) 2, Short.class);
// evaluate("new double[]{1d,2d,3d,4d}[0]", (double) 1, Double.class);
// evaluate("new float[]{1f,2f,3f,4f}[0]", (float) 1, Float.class);
// evaluate("new byte[]{1,2,3,4}[0]", (byte) 1, Byte.class);
// }
//
// public void testErrorCases() {
// evaluateAndCheckError("new char[7]{'a','c','d','e'}", SpelMessages.INITIALIZER_LENGTH_INCORRECT);
// evaluateAndCheckError("new char[3]{'a','c','d','e'}", SpelMessages.INITIALIZER_LENGTH_INCORRECT);
// evaluateAndCheckError("new char[2]{'hello','world'}", SpelMessages.TYPE_CONVERSION_ERROR);
// evaluateAndCheckError("new String('a','c','d')", SpelMessages.CONSTRUCTOR_NOT_FOUND);
// }
//
// public void testTypeArrayConstructors() {
// evaluate("new String[]{'a','b','c','d'}[1]", "b", String.class);
// evaluateAndCheckError("new String[]{'a','b','c','d'}.size()", SpelMessages.METHOD_NOT_FOUND, 30, "size()",
// "java.lang.String[]");
// evaluateAndCheckError("new String[]{'a','b','c','d'}.juggernaut", SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, 30,
// "juggernaut", "java.lang.String[]");
// evaluate("new String[]{'a','b','c','d'}.length", 4, Integer.class);
// }
// public void testMultiDimensionalArrays() {
// evaluate(
// "new String[3,4]",
// "[Ljava.lang.String;[3]{java.lang.String[4]{null,null,null,null},java.lang.String[4]{null,null,null,null},java.lang.String[4]{null,null,null,null}}"
// ,
// new String[3][4].getClass());
// }
//
//
// evaluate("new String(new char[]{'h','e','l','l','o'})", "hello", String.class);
//
//
//
// public void testRelOperatorsIn01() {
// evaluate("3 in {1,2,3,4,5}", "true", Boolean.class);
// }
//
// public void testRelOperatorsIn02() {
// evaluate("name in {null, \"Nikola Tesla\"}", "true", Boolean.class);
// evaluate("name in {null, \"Anonymous\"}", "false", Boolean.class);
// }
//
//
// public void testRelOperatorsBetween02() {
// evaluate("'efg' between {'abc', 'xyz'}", "true", Boolean.class);
// }
//
//
// public void testRelOperatorsBetweenErrors02() {
// evaluateAndCheckError("'abc' between {5,7}", SpelMessages.NOT_COMPARABLE, 6);
// }
// Lambda calculations
//
//
// public void testLambda02() {
// evaluate("(#max={|x,y| $x > $y ? $x : $y };true)", "true", Boolean.class);
// }
//
// public void testLambdaMax() {
// evaluate("(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", "25", Integer.class);
// }
//
// public void testLambdaFactorial01() {
// evaluate("(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))", "120", Integer.class);
// }
//
// public void testLambdaFactorial02() {
// evaluate("(#fact = {|n| $n <= 1 ? 1 : #fact($n-1) * $n }; #fact(5))", "120", Integer.class);
// }
//
// public void testLambdaAlphabet01() {
// evaluate("(#alpha = {|l,s| $l>'z'?$s:#alpha($l+1,$s+$l)};#alphabet={||#alpha('a','')}; #alphabet())",
// "abcdefghijklmnopqrstuvwxyz", String.class);
// }
//
// public void testLambdaAlphabet02() {
// evaluate("(#alphabet = {|l,s| $l>'z'?$s:#alphabet($l+1,$s+$l)};#alphabet('a',''))",
// "abcdefghijklmnopqrstuvwxyz", String.class);
// }
//
// public void testLambdaDelegation01() {
// evaluate("(#sqrt={|n| T(Math).sqrt($n)};#delegate={|f,n| $f($n)};#delegate(#sqrt,4))", "2.0", Double.class);
// }
//
// public void testVariableReferences() {
// evaluate("(#answer=42;#answer)", "42", Integer.class, true);
// evaluate("($answer=42;$answer)", "42", Integer.class, true);
// }
// // inline map creation
// @Test
// public void testInlineMapCreation01() {
// evaluate("#{'key1':'Value 1', 'today':'Monday'}", "{key1=Value 1, today=Monday}", HashMap.class);
// }
//
// @Test
// public void testInlineMapCreation02() {
// // "{2=February, 1=January, 3=March}", HashMap.class);
// evaluate("#{1:'January', 2:'February', 3:'March'}.size()", 3, Integer.class);
// }
//
// @Test
// public void testInlineMapCreation03() {
// evaluate("#{'key1':'Value 1', 'today':'Monday'}['key1']", "Value 1", String.class);
// }
//
// @Test
// public void testInlineMapCreation04() {
// evaluate("#{1:'January', 2:'February', 3:'March'}[3]", "March", String.class);
// }
//
// @Test
// public void testInlineMapCreation05() {
// evaluate("#{1:'January', 2:'February', 3:'March'}.get(2)", "February", String.class);
// }
// set construction
@Test
public void testSetConstruction01() {
evaluate("new java.util.HashSet().addAll({'a','b','c'})", "true", Boolean.class);
}
//
// public void testConstructorInvocation02() {
// evaluate("new String[3]", "java.lang.String[3]{null,null,null}", String[].class);
// }
//
// public void testConstructorInvocation03() {
// evaluateAndCheckError("new String[]", SpelMessages.NO_SIZE_OR_INITIALIZER_FOR_ARRAY_CONSTRUCTION, 4);
// }
//
// public void testConstructorInvocation04() {
// evaluateAndCheckError("new String[3]{'abc',3,'def'}", SpelMessages.INCORRECT_ELEMENT_TYPE_FOR_ARRAY, 4);
// }
// array construction
// @Test
// public void testArrayConstruction01() {
// evaluate("new int[] {1, 2, 3, 4, 5}", "int[5]{1,2,3,4,5}", int[].class);
// }
// public void testArrayConstruction02() {
// evaluate("new String[] {'abc', 'xyz'}", "java.lang.String[2]{abc,xyz}", String[].class);
// }
//
// collection processors
// from spring.net: count,sum,max,min,average,sort,orderBy,distinct,nonNull
// public void testProcessorsCount01() {
// evaluate("new String[] {'abc','def','xyz'}.count()", "3", Integer.class);
// }
//
// public void testProcessorsCount02() {
// evaluate("new int[] {1,2,3}.count()", "3", Integer.class);
// }
//
// public void testProcessorsMax01() {
// evaluate("new int[] {1,2,3}.max()", "3", Integer.class);
// }
//
// public void testProcessorsMin01() {
// evaluate("new int[] {1,2,3}.min()", "1", Integer.class);
// }
//
// public void testProcessorsKeys01() {
// evaluate("#{1:'January', 2:'February', 3:'March'}.keySet().sort()", "[1, 2, 3]", ArrayList.class);
// }
//
// public void testProcessorsValues01() {
// evaluate("#{1:'January', 2:'February', 3:'March'}.values().sort()", "[February, January, March]",
// ArrayList.class);
// }
//
// public void testProcessorsAverage01() {
// evaluate("new int[] {1,2,3}.average()", "2", Integer.class);
// }
//
// public void testProcessorsSort01() {
// evaluate("new int[] {3,2,1}.sort()", "int[3]{1,2,3}", int[].class);
// }
//
// public void testCollectionProcessorsNonNull01() {
// evaluate("{'a','b',null,'d',null}.nonnull()", "[a, b, d]", ArrayList.class);
// }
//
// public void testCollectionProcessorsDistinct01() {
// evaluate("{'a','b','a','d','e'}.distinct()", "[a, b, d, e]", ArrayList.class);
// }
//
// public void testProjection03() {
// evaluate("{1,2,3,4,5,6,7,8,9,10}.!{#this>5}",
// "[false, false, false, false, false, true, true, true, true, true]", ArrayList.class);
// }
//
// public void testProjection04() {
// evaluate("{1,2,3,4,5,6,7,8,9,10}.!{$index>5?'y':'n'}", "[n, n, n, n, n, n, y, y, y, y]", ArrayList.class);
// }
// Bean references
// public void testReferences01() {
// evaluate("@(apple).name", "Apple", String.class, true);
// }
//
// public void testReferences02() {
// evaluate("@(fruits:banana).name", "Banana", String.class, true);
// }
//
// public void testReferences03() {
// evaluate("@(a.b.c)", null, null);
// } // null - no context, a.b.c treated as name
//
// public void testReferences05() {
// evaluate("@(a/b/c:orange).name", "Orange", String.class, true);
// }
//
// public void testReferences06() {
// evaluate("@(apple).color.getRGB() == T(java.awt.Color).green.getRGB()", "true", Boolean.class);
// }
//
// public void testReferences07() {
// evaluate("@(apple).color.getRGB().equals(T(java.awt.Color).green.getRGB())", "true", Boolean.class);
// }
//
// value is not public, it is accessed through getRGB()
// public void testStaticRef01() {
// evaluate("T(Color).green.value!=0", "true", Boolean.class);
// }
// Indexer
// public void testCutProcessor01() {
// evaluate("{1,2,3,4,5}.cut(1,3)", "[2, 3, 4]", ArrayList.class);
// }
//
// public void testCutProcessor02() {
// evaluate("{1,2,3,4,5}.cut(3,1)", "[4, 3, 2]", ArrayList.class);
// }
// Ternary operator
// public void testTernaryOperator01() {
// evaluate("{1}.#isEven(#this[0]) == 'y'?'it is even':'it is odd'", "it is odd", String.class);
// }
//
// public void testTernaryOperator02() {
// evaluate("{2}.#isEven(#this[0]) == 'y'?'it is even':'it is odd'", "it is even", String.class);
// }
// public void testSelectionUsingIndex() {
// evaluate("{1,2,3,4,5,6,7,8,9,10}.?{$index > 5 }", "[7, 8, 9, 10]", ArrayList.class);
// }
// public void testSelection01() {
// inline list creation not supported:
// evaluate("{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'}", "[2, 4, 6, 8, 10]", ArrayList.class);
// }
//
// public void testSelectionUsingIndex() {
// evaluate("listOfNumbersUpToTen.?[#index > 5 ]", "[7, 8, 9, 10]", ArrayList.class);
// }
}

View File

@@ -0,0 +1,359 @@
package org.springframework.expression.spel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class IndexingTests {
@Test
public void indexIntoGenericPropertyContainingMap() {
Map<String, String> property = new HashMap<String, String>();
property.put("foo", "bar");
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.HashMap<?, ?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
assertEquals(property, expression.getValue(this, Map.class));
expression = parser.parseExpression("property['foo']");
assertEquals("bar", expression.getValue(this));
}
@FieldAnnotation
public Object property;
@Test
public void indexIntoGenericPropertyContainingMapObject() {
Map<String, Map<String, String>> property = new HashMap<String, Map<String, String>>();
Map<String, String> map = new HashMap<String, String>();
map.put("foo", "bar");
property.put("property", map);
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.addPropertyAccessor(new MapAccessor());
context.setRootObject(property);
Expression expression = parser.parseExpression("property");
assertEquals("java.util.HashMap<?, ?>", expression.getValueTypeDescriptor(context).toString());
assertEquals(map, expression.getValue(context));
assertEquals(map, expression.getValue(context, Map.class));
expression = parser.parseExpression("property['foo']");
assertEquals("bar", expression.getValue(context));
}
public static class MapAccessor implements PropertyAccessor {
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
return (((Map) target).containsKey(name));
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((Map) target).get(name));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
return true;
}
@SuppressWarnings("unchecked")
public void write(EvaluationContext context, Object target, String name, Object newValue)
throws AccessException {
((Map) target).put(name, newValue);
}
public Class<?>[] getSpecificTargetClasses() {
return new Class[] { Map.class };
}
}
@Test
public void setGenericPropertyContainingMap() {
Map<String, String> property = new HashMap<String, String>();
property.put("foo", "bar");
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.HashMap<?, ?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property['foo']");
assertEquals("bar", expression.getValue(this));
expression.setValue(this, "baz");
assertEquals("baz", expression.getValue(this));
}
@Test
public void setPropertyContainingMap() {
Map<Integer, Integer> property = new HashMap<Integer, Integer>();
property.put(9, 3);
this.parameterizedMap = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("parameterizedMap");
assertEquals("java.util.HashMap<java.lang.Integer, java.lang.Integer>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("parameterizedMap['9']");
assertEquals(3, expression.getValue(this));
expression.setValue(this, "37");
assertEquals(37, expression.getValue(this));
}
public Map<Integer, Integer> parameterizedMap;
@Test
public void setPropertyContainingMapAutoGrow() {
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, false));
Expression expression = parser.parseExpression("parameterizedMap");
assertEquals("java.util.Map<java.lang.Integer, java.lang.Integer>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("parameterizedMap['9']");
assertEquals(null, expression.getValue(this));
expression.setValue(this, "37");
assertEquals(37, expression.getValue(this));
}
@Test
public void indexIntoGenericPropertyContainingList() {
List<String> property = new ArrayList<String>();
property.add("bar");
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
assertEquals("bar", expression.getValue(this));
}
@Test
public void setGenericPropertyContainingList() {
List<Integer> property = new ArrayList<Integer>();
property.add(3);
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
assertEquals(3, expression.getValue(this));
expression.setValue(this, "4");
assertEquals("4", expression.getValue(this));
}
@Test
public void setGenericPropertyContainingListAutogrow() {
List<Integer> property = new ArrayList<Integer>();
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
try {
expression.setValue(this, "4");
} catch (EvaluationException e) {
assertTrue(e.getMessage().startsWith("EL1053E"));
}
}
@Test
public void indexIntoPropertyContainingList() {
List<Integer> property = new ArrayList<Integer>();
property.add(3);
this.parameterizedList = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("parameterizedList");
assertEquals("java.util.ArrayList<java.lang.Integer>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("parameterizedList[0]");
assertEquals(3, expression.getValue(this));
}
public List<Integer> parameterizedList;
@Test
public void indexIntoPropertyContainingListOfList() {
List<List<Integer>> property = new ArrayList<List<Integer>>();
property.add(Arrays.asList(3));
this.parameterizedListOfList = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("parameterizedListOfList[0]");
assertEquals("java.util.Arrays$ArrayList<java.lang.Integer>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property.get(0), expression.getValue(this));
expression = parser.parseExpression("parameterizedListOfList[0][0]");
assertEquals(3, expression.getValue(this));
}
public List<List<Integer>> parameterizedListOfList;
@Test
public void setPropertyContainingList() {
List<Integer> property = new ArrayList<Integer>();
property.add(3);
this.parameterizedList = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("parameterizedList");
assertEquals("java.util.ArrayList<java.lang.Integer>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("parameterizedList[0]");
assertEquals(3, expression.getValue(this));
expression.setValue(this, "4");
assertEquals(4, expression.getValue(this));
}
@Test
public void indexIntoGenericPropertyContainingNullList() {
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.lang.Object", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
try {
assertEquals("bar", expression.getValue(this));
} catch (EvaluationException e) {
assertTrue(e.getMessage().startsWith("EL1027E"));
}
}
@Test
public void indexIntoGenericPropertyContainingGrowingList() {
List<String> property = new ArrayList<String>();
this.property = property;
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
try {
assertEquals("bar", expression.getValue(this));
} catch (EvaluationException e) {
assertTrue(e.getMessage().startsWith("EL1053E"));
}
}
@Test
public void indexIntoGenericPropertyContainingGrowingList2() {
List<String> property2 = new ArrayList<String>();
this.property2 = property2;
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("property2");
assertEquals("java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals(property2, expression.getValue(this));
expression = parser.parseExpression("property2[0]");
try {
assertEquals("bar", expression.getValue(this));
} catch (EvaluationException e) {
assertTrue(e.getMessage().startsWith("EL1053E"));
}
}
public List property2;
@Test
public void indexIntoGenericPropertyContainingArray() {
String[] property = new String[] { "bar" };
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("property");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.lang.String[]", expression.getValueTypeDescriptor(this).toString());
assertEquals(property, expression.getValue(this));
expression = parser.parseExpression("property[0]");
assertEquals("bar", expression.getValue(this));
}
@Test
public void emptyList() {
listOfScalarNotGeneric = new ArrayList();
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("listOfScalarNotGeneric");
assertEquals("java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals("", expression.getValue(this, String.class));
}
@Test
public void resolveCollectionElementType() {
listNotGeneric = new ArrayList();
listNotGeneric.add(5);
listNotGeneric.add(6);
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("listNotGeneric");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList<?>", expression.getValueTypeDescriptor(this).toString());
assertEquals("5,6", expression.getValue(this, String.class));
}
@Test
public void resolveCollectionElementTypeNull() {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("listNotGeneric");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.List<?>", expression.getValueTypeDescriptor(this).toString());
}
@FieldAnnotation
public List listNotGeneric;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnnotation {
}
@Test
public void resolveMapKeyValueTypes() {
mapNotGeneric = new HashMap();
mapNotGeneric.put("baseAmount", 3.11);
mapNotGeneric.put("bonusAmount", 7.17);
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("mapNotGeneric");
assertEquals("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.HashMap<?, ?>", expression.getValueTypeDescriptor(this).toString());
}
@FieldAnnotation
public Map mapNotGeneric;
@Test
public void testListOfScalar() {
listOfScalarNotGeneric = new ArrayList();
listOfScalarNotGeneric.add("5");
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("listOfScalarNotGeneric[0]");
assertEquals(new Integer(5), expression.getValue(this, Integer.class));
}
public List listOfScalarNotGeneric;
@Test
public void testListsOfMap() {
listOfMapsNotGeneric = new ArrayList();
Map map = new HashMap();
map.put("fruit", "apple");
listOfMapsNotGeneric.add(map);
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("listOfMapsNotGeneric[0]['fruit']");
assertEquals("apple", expression.getValue(this, String.class));
}
public List listOfMapsNotGeneric;
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2002-2010 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.util.ArrayList;
import java.util.Collections;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.expression.spel.ast.InlineList;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* Test usage of inline lists.
*
* @author Andy Clement
* @since 3.0.4
*/
public class ListTests extends ExpressionTestCase {
// if the list is full of literals then it will be of the type unmodifiableClass rather than ArrayList
Class<?> unmodifiableClass = Collections.unmodifiableList(new ArrayList<Object>()).getClass();
@Test
public void testInlineListCreation01() {
evaluate("{1, 2, 3, 4, 5}", "[1, 2, 3, 4, 5]", unmodifiableClass);
}
@Test
public void testInlineListCreation02() {
evaluate("{'abc', 'xyz'}", "[abc, xyz]", unmodifiableClass);
}
@Test
public void testInlineListCreation03() {
evaluate("{}", "[]", unmodifiableClass);
}
@Test
public void testInlineListCreation04() {
evaluate("{'abc'=='xyz'}", "[false]", ArrayList.class);
}
@Test
public void testInlineListAndNesting() {
evaluate("{{1,2,3},{4,5,6}}", "[[1, 2, 3], [4, 5, 6]]", unmodifiableClass);
evaluate("{{1,'2',3},{4,{'a','b'},5,6}}", "[[1, 2, 3], [4, [a, b], 5, 6]]", unmodifiableClass);
}
@Test
public void testInlineListError() {
parseAndCheckError("{'abc'", SpelMessage.OOD);
}
@Test
public void testRelOperatorsIs02() {
evaluate("{1, 2, 3, 4, 5} instanceof T(java.util.List)", "true", Boolean.class);
}
@Test
public void testInlineListCreation05() {
evaluate("3 between {1,5}", "true", Boolean.class);
}
@Test
public void testInlineListCreation06() {
evaluate("8 between {1,5}", "false", Boolean.class);
}
@Test
public void testInlineListAndProjectionSelection() {
evaluate("{1,2,3,4,5,6}.![#this>3]", "[false, false, false, true, true, true]", ArrayList.class);
evaluate("{1,2,3,4,5,6}.?[#this>3]", "[4, 5, 6]", ArrayList.class);
evaluate("{1,2,3,4,5,6,7,8,9,10}.?[#isEven(#this) == 'y']", "[2, 4, 6, 8, 10]", ArrayList.class);
}
@Test
public void testSetConstruction01() {
evaluate("new java.util.HashSet().addAll({'a','b','c'})", "true", Boolean.class);
}
@Test
public void testRelOperatorsBetween01() {
evaluate("32 between {32, 42}", "true", Boolean.class);
}
@Test
public void testRelOperatorsBetween02() {
evaluate("'efg' between {'abc', 'xyz'}", "true", Boolean.class);
}
@Test
public void testRelOperatorsBetween03() {
evaluate("42 between {32, 42}", "true", Boolean.class);
}
@Test
public void testRelOperatorsBetweenErrors02() {
evaluateAndCheckError("'abc' between {5,7}", SpelMessage.NOT_COMPARABLE, 6);
}
@Test
public void testConstantRepresentation1() {
checkConstantList("{1,2,3,4,5}", true);
checkConstantList("{'abc'}", true);
checkConstantList("{}", true);
checkConstantList("{#a,2,3}", false);
checkConstantList("{1,2,Integer.valueOf(4)}", false);
checkConstantList("{1,2,{#a}}", false);
}
private void checkConstantList(String expressionText, boolean expectedToBeConstant) {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expression = (SpelExpression) parser.parseExpression(expressionText);
SpelNode node = expression.getAST();
Assert.assertTrue(node instanceof InlineList);
InlineList inlineList = (InlineList) node;
if (expectedToBeConstant) {
Assert.assertTrue(inlineList.isConstant());
} else {
Assert.assertFalse(inlineList.isConstant());
}
}
@Test
public void testInlineListWriting() {
// list should be unmodifiable
try {
evaluate("{1, 2, 3, 4, 5}[0]=6", "[1, 2, 3, 4, 5]", unmodifiableClass);
Assert.fail();
} catch (UnsupportedOperationException uoe) {
// success
}
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2002-2009 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 junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* @author Andy Clement
*/
public class LiteralExpressionTests {
@Test
public void testGetValue() throws Exception {
LiteralExpression lEx = new LiteralExpression("somevalue");
checkString("somevalue", lEx.getValue());
checkString("somevalue", lEx.getValue(String.class));
EvaluationContext ctx = new StandardEvaluationContext();
checkString("somevalue", lEx.getValue(ctx));
checkString("somevalue", lEx.getValue(ctx, String.class));
checkString("somevalue", lEx.getValue(new Rooty()));
checkString("somevalue", lEx.getValue(new Rooty(), String.class));
checkString("somevalue", lEx.getValue(ctx, new Rooty()));
checkString("somevalue", lEx.getValue(ctx, new Rooty(),String.class));
Assert.assertEquals("somevalue", lEx.getExpressionString());
Assert.assertFalse(lEx.isWritable(new StandardEvaluationContext()));
Assert.assertFalse(lEx.isWritable(new Rooty()));
Assert.assertFalse(lEx.isWritable(new StandardEvaluationContext(), new Rooty()));
}
static class Rooty {}
@Test
public void testSetValue() {
try {
LiteralExpression lEx = new LiteralExpression("somevalue");
lEx.setValue(new StandardEvaluationContext(), "flibble");
Assert.fail("Should have got an exception that the value cannot be set");
}
catch (EvaluationException ee) {
// success, not allowed - whilst here, check the expression value in the exception
Assert.assertEquals(ee.getExpressionString(), "somevalue");
}
try {
LiteralExpression lEx = new LiteralExpression("somevalue");
lEx.setValue(new Rooty(), "flibble");
Assert.fail("Should have got an exception that the value cannot be set");
}
catch (EvaluationException ee) {
// success, not allowed - whilst here, check the expression value in the exception
Assert.assertEquals(ee.getExpressionString(), "somevalue");
}
try {
LiteralExpression lEx = new LiteralExpression("somevalue");
lEx.setValue(new StandardEvaluationContext(), new Rooty(), "flibble");
Assert.fail("Should have got an exception that the value cannot be set");
}
catch (EvaluationException ee) {
// success, not allowed - whilst here, check the expression value in the exception
Assert.assertEquals(ee.getExpressionString(), "somevalue");
}
}
@Test
public void testGetValueType() throws Exception {
LiteralExpression lEx = new LiteralExpression("somevalue");
Assert.assertEquals(String.class, lEx.getValueType());
Assert.assertEquals(String.class, lEx.getValueType(new StandardEvaluationContext()));
Assert.assertEquals(String.class, lEx.getValueType(new Rooty()));
Assert.assertEquals(String.class, lEx.getValueType(new StandardEvaluationContext(), new Rooty()));
Assert.assertEquals(String.class, lEx.getValueTypeDescriptor().getType());
Assert.assertEquals(String.class, lEx.getValueTypeDescriptor(new StandardEvaluationContext()).getType());
Assert.assertEquals(String.class, lEx.getValueTypeDescriptor(new Rooty()).getType());
Assert.assertEquals(String.class, lEx.getValueTypeDescriptor(new StandardEvaluationContext(), new Rooty()).getType());
}
private void checkString(String expectedString, Object value) {
if (!(value instanceof String)) {
Assert.fail("Result was not a string, it was of type " + value.getClass() + " (value=" + value + ")");
}
if (!((String) value).equals(expectedString)) {
Assert.fail("Did not get expected result. Should have been '" + expectedString + "' but was '" + value + "'");
}
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2002-2009 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 junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.standard.SpelExpression;
/**
* Tests the evaluation of basic literals: boolean, integer, hex integer, long, real, null, date
*
* @author Andy Clement
*/
public class LiteralTests extends ExpressionTestCase {
@Test
public void testLiteralBoolean01() {
evaluate("false", "false", Boolean.class);
}
@Test
public void testLiteralBoolean02() {
evaluate("true", "true", Boolean.class);
}
@Test
public void testLiteralInteger01() {
evaluate("1", "1", Integer.class);
}
@Test
public void testLiteralInteger02() {
evaluate("1415", "1415", Integer.class);
}
@Test
public void testLiteralString01() {
evaluate("'Hello World'", "Hello World", String.class);
}
@Test
public void testLiteralString02() {
evaluate("'joe bloggs'", "joe bloggs", String.class);
}
@Test
public void testLiteralString03() {
evaluate("'hello'", "hello", String.class);
}
@Test
public void testLiteralString04() {
evaluate("'Tony''s Pizza'", "Tony's Pizza", String.class);
evaluate("'Tony\\r''s Pizza'", "Tony\\r's Pizza", String.class);
}
@Test
public void testLiteralString05() {
evaluate("\"Hello World\"", "Hello World", String.class);
}
@Test
public void testLiteralString06() {
evaluate("\"Hello ' World\"", "Hello ' World", String.class);
}
@Test
public void testHexIntLiteral01() {
evaluate("0x7FFFF", "524287", Integer.class);
evaluate("0x7FFFFL", 524287L, Long.class);
evaluate("0X7FFFF", "524287", Integer.class);
evaluate("0X7FFFFl", 524287L, Long.class);
}
@Test
public void testLongIntLiteral01() {
evaluate("0xCAFEBABEL", 3405691582L, Long.class);
}
@Test
public void testLongIntInteractions01() {
evaluate("0x20 * 2L", 64L, Long.class);
// ask for the result to be made into an Integer
evaluateAndAskForReturnType("0x20 * 2L", 64, Integer.class);
// ask for the result to be made into an Integer knowing that it will not fit
evaluateAndCheckError("0x1220 * 0xffffffffL", Integer.class, SpelMessage.TYPE_CONVERSION_ERROR, 0);
}
@Test
public void testSignedIntLiterals() {
evaluate("-1", -1, Integer.class);
evaluate("-0xa", -10, Integer.class);
evaluate("-1L", -1L, Long.class);
evaluate("-0x20l", -32L, Long.class);
}
@Test
public void testLiteralReal01_CreatingDoubles() {
evaluate("1.25", 1.25d, Double.class);
evaluate("2.99", 2.99d, Double.class);
evaluate("-3.141", -3.141d, Double.class);
evaluate("1.25d", 1.25d, Double.class);
evaluate("2.99d", 2.99d, Double.class);
evaluate("-3.141d", -3.141d, Double.class);
evaluate("1.25D", 1.25d, Double.class);
evaluate("2.99D", 2.99d, Double.class);
evaluate("-3.141D", -3.141d, Double.class);
}
@Test
public void testLiteralReal02_CreatingFloats() {
// For now, everything becomes a double...
evaluate("1.25f", 1.25d, Double.class);
evaluate("2.5f", 2.5d, Double.class);
evaluate("-3.5f", -3.5d, Double.class);
evaluate("1.25F", 1.25d, Double.class);
evaluate("2.5F", 2.5d, Double.class);
evaluate("-3.5F", -3.5d, Double.class);
}
@Test
public void testLiteralReal03_UsingExponents() {
evaluate("6.0221415E+23", "6.0221415E23", Double.class);
evaluate("6.0221415e+23", "6.0221415E23", Double.class);
evaluate("6.0221415E+23d", "6.0221415E23", Double.class);
evaluate("6.0221415e+23D", "6.0221415E23", Double.class);
evaluate("6E2f", 600.0d, Double.class);
}
@Test
public void testLiteralReal04_BadExpressions() {
parseAndCheckError("6.1e23e22", SpelMessage.MORE_INPUT, 6, "e22");
parseAndCheckError("6.1f23e22", SpelMessage.MORE_INPUT, 4, "23e22");
}
@Test
public void testLiteralNull01() {
evaluate("null", null, null);
}
@Test
public void testConversions() {
// getting the expression type to be what we want - either:
evaluate("new Integer(37).byteValue()", (byte) 37, Byte.class); // calling byteValue() on Integer.class
evaluateAndAskForReturnType("new Integer(37)", (byte) 37, Byte.class); // relying on registered type converters
}
@Test
public void testNotWritable() throws Exception {
SpelExpression expr = (SpelExpression)parser.parseExpression("37");
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
expr = (SpelExpression)parser.parseExpression("37L");
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
expr = (SpelExpression)parser.parseExpression("true");
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
}
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright 2002-2009 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.util.Map;
import java.util.HashMap;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* Testing variations on map access.
*
* @author Andy Clement
*/
public class MapAccessTests extends ExpressionTestCase {
@Test
public void testSimpleMapAccess01() {
evaluate("testMap.get('monday')", "montag", String.class);
}
@Test
public void testMapAccessThroughIndexer() {
evaluate("testMap['monday']", "montag", String.class);
}
@Test
public void testCustomMapAccessor() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = TestScenarioCreator.getTestEvaluationContext();
ctx.addPropertyAccessor(new MapAccessor());
Expression expr = parser.parseExpression("testMap.monday");
Object value = expr.getValue(ctx, String.class);
Assert.assertEquals("montag", value);
}
@Test
public void testVariableMapAccess() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = TestScenarioCreator.getTestEvaluationContext();
ctx.setVariable("day", "saturday");
Expression expr = parser.parseExpression("testMap[#day]");
Object value = expr.getValue(ctx, String.class);
Assert.assertEquals("samstag", value);
}
@Test
public void testGetValue(){
Map props1= new HashMap<String,String>();
props1.put("key1", "value1");
props1.put("key2", "value2");
props1.put("key3", "value3");
Object bean = new TestBean("name1",new TestBean("name2",null,"Description 2",15,props1),"description 1", 6,props1);
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("testBean.properties['key2']");
String key= (String)exp.getValue(bean);
}
public static class TestBean
{
private String name;
private TestBean testBean;
private String description;
private Integer priority;
private Map properties;
public TestBean() {
super();
}
public TestBean(String name, TestBean testBean, String description,Integer priority,Map props) {
super();
this.name = name;
this.testBean = testBean;
this.description = description;
this.priority=priority;
this.properties=props;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TestBean getTestBean() {
return testBean;
}
public void setTestBean(TestBean testBean) {
this.testBean = testBean;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
public Map getProperties() {
return properties;
}
public void setProperties(Map properties) {
this.properties = properties;
}
}
public static class MapAccessor implements PropertyAccessor {
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
return (((Map) target).containsKey(name));
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((Map) target).get(name));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
return true;
}
@SuppressWarnings("unchecked")
public void write(EvaluationContext context, Object target, String name, Object newValue)
throws AccessException {
((Map) target).put(name, newValue);
}
public Class<?>[] getSpecificTargetClasses() {
return new Class[] { Map.class };
}
}
}

View File

@@ -0,0 +1,364 @@
/*
* Copyright 2002-2009 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.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionInvocationTargetException;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodFilter;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.testresources.PlaceOfBirth;
/**
* Tests invocation of methods.
*
* @author Andy Clement
*/
public class MethodInvocationTests extends ExpressionTestCase {
@Test
public void testSimpleAccess01() {
evaluate("getPlaceOfBirth().getCity()", "SmilJan", String.class);
}
// public void testBuiltInProcessors() {
// evaluate("new int[]{1,2,3,4}.count()", 4, Integer.class);
// evaluate("new int[]{4,3,2,1}.sort()[3]", 4, Integer.class);
// evaluate("new int[]{4,3,2,1}.average()", 2, Integer.class);
// evaluate("new int[]{4,3,2,1}.max()", 4, Integer.class);
// evaluate("new int[]{4,3,2,1}.min()", 1, Integer.class);
// evaluate("new int[]{4,3,2,1,2,3}.distinct().count()", 4, Integer.class);
// evaluate("{1,2,3,null}.nonnull().count()", 3, Integer.class);
// evaluate("new int[]{4,3,2,1,2,3}.distinct().count()", 4, Integer.class);
// }
@Test
public void testStringClass() {
evaluate("new java.lang.String('hello').charAt(2)", 'l', Character.class);
evaluate("new java.lang.String('hello').charAt(2).equals('l'.charAt(0))", true, Boolean.class);
evaluate("'HELLO'.toLowerCase()", "hello", String.class);
evaluate("' abcba '.trim()", "abcba", String.class);
}
@Test
public void testNonExistentMethods() {
// name is ok but madeup() does not exist
evaluateAndCheckError("name.madeup()", SpelMessage.METHOD_NOT_FOUND, 5);
}
@Test
public void testWidening01() {
// widening of int 3 to double 3 is OK
evaluate("new Double(3.0d).compareTo(8)", -1, Integer.class);
evaluate("new Double(3.0d).compareTo(3)", 0, Integer.class);
evaluate("new Double(3.0d).compareTo(2)", 1, Integer.class);
}
@Test
public void testArgumentConversion01() {
// Rely on Double>String conversion for calling startsWith()
evaluate("new String('hello 2.0 to you').startsWith(7.0d)", false, Boolean.class);
evaluate("new String('7.0 foobar').startsWith(7.0d)", true, Boolean.class);
}
@Test
public void testMethodThrowingException_SPR6760() {
// Test method on inventor: throwException()
// On 1 it will throw an IllegalArgumentException
// On 2 it will throw a RuntimeException
// On 3 it will exit normally
// In each case it increments the Inventor field 'counter' when invoked
SpelExpressionParser parser = new SpelExpressionParser();
Expression expr = parser.parseExpression("throwException(#bar)");
// Normal exit
StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
eContext.setVariable("bar",3);
Object o = expr.getValue(eContext);
Assert.assertEquals(o,3);
Assert.assertEquals(1,parser.parseExpression("counter").getValue(eContext));
// Now the expression has cached that throwException(int) is the right thing to call
// Let's change 'bar' to be a PlaceOfBirth which indicates the cached reference is
// out of date.
eContext.setVariable("bar",new PlaceOfBirth("London"));
o = expr.getValue(eContext);
Assert.assertEquals("London", o);
// That confirms the logic to mark the cached reference stale and retry is working
// Now let's cause the method to exit via exception and ensure it doesn't cause
// a retry.
// First, switch back to throwException(int)
eContext.setVariable("bar",3);
o = expr.getValue(eContext);
Assert.assertEquals(3, o);
Assert.assertEquals(2,parser.parseExpression("counter").getValue(eContext));
// Now cause it to throw an exception:
eContext.setVariable("bar",1);
try {
o = expr.getValue(eContext);
Assert.fail();
} catch (Exception e) {
if (e instanceof SpelEvaluationException) {
e.printStackTrace();
Assert.fail("Should not be a SpelEvaluationException");
}
// normal
}
// If counter is 4 then the method got called twice!
Assert.assertEquals(3,parser.parseExpression("counter").getValue(eContext));
eContext.setVariable("bar",4);
try {
o = expr.getValue(eContext);
Assert.fail();
} catch (Exception e) {
// 4 means it will throw a checked exception - this will be wrapped
if (!(e instanceof ExpressionInvocationTargetException)) {
e.printStackTrace();
Assert.fail("Should have been wrapped");
}
// normal
}
// If counter is 5 then the method got called twice!
Assert.assertEquals(4,parser.parseExpression("counter").getValue(eContext));
}
/**
* Check on first usage (when the cachedExecutor in MethodReference is null) that the exception is not wrapped.
*/
@Test
public void testMethodThrowingException_SPR6941() {
// Test method on inventor: throwException()
// On 1 it will throw an IllegalArgumentException
// On 2 it will throw a RuntimeException
// On 3 it will exit normally
// In each case it increments the Inventor field 'counter' when invoked
SpelExpressionParser parser = new SpelExpressionParser();
Expression expr = parser.parseExpression("throwException(#bar)");
eContext.setVariable("bar",2);
try {
expr.getValue(eContext);
Assert.fail();
} catch (Exception e) {
if (e instanceof SpelEvaluationException) {
e.printStackTrace();
Assert.fail("Should not be a SpelEvaluationException");
}
// normal
}
}
@Test
public void testMethodThrowingException_SPR6941_2() {
// Test method on inventor: throwException()
// On 1 it will throw an IllegalArgumentException
// On 2 it will throw a RuntimeException
// On 3 it will exit normally
// In each case it increments the Inventor field 'counter' when invoked
SpelExpressionParser parser = new SpelExpressionParser();
Expression expr = parser.parseExpression("throwException(#bar)");
eContext.setVariable("bar",4);
try {
expr.getValue(eContext);
Assert.fail();
} catch (ExpressionInvocationTargetException e) {
Throwable t = e.getCause();
Assert.assertEquals("org.springframework.expression.spel.testresources.Inventor$TestException", t.getClass().getName());
return;
}
Assert.fail("Should not be a SpelEvaluationException");
}
@Test
public void testMethodFiltering_SPR6764() {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(new TestObject());
LocalFilter filter = new LocalFilter();
context.registerMethodFilter(TestObject.class,filter);
// Filter will be called but not do anything, so first doit() will be invoked
SpelExpression expr = (SpelExpression) parser.parseExpression("doit(1)");
String result = expr.getValue(context,String.class);
Assert.assertEquals("1",result);
Assert.assertTrue(filter.filterCalled);
// Filter will now remove non @Anno annotated methods
filter.removeIfNotAnnotated = true;
filter.filterCalled = false;
expr = (SpelExpression) parser.parseExpression("doit(1)");
result = expr.getValue(context,String.class);
Assert.assertEquals("double 1.0",result);
Assert.assertTrue(filter.filterCalled);
// check not called for other types
filter.filterCalled=false;
context.setRootObject(new String("abc"));
expr = (SpelExpression) parser.parseExpression("charAt(0)");
result = expr.getValue(context,String.class);
Assert.assertEquals("a",result);
Assert.assertFalse(filter.filterCalled);
// check de-registration works
filter.filterCalled = false;
context.registerMethodFilter(TestObject.class,null);//clear filter
context.setRootObject(new TestObject());
expr = (SpelExpression) parser.parseExpression("doit(1)");
result = expr.getValue(context,String.class);
Assert.assertEquals("1",result);
Assert.assertFalse(filter.filterCalled);
}
// Simple filter
static class LocalFilter implements MethodFilter {
public boolean removeIfNotAnnotated = false;
public boolean filterCalled = false;
private boolean isAnnotated(Method m) {
Annotation[] annos = m.getAnnotations();
if (annos==null) {
return false;
}
for (Annotation anno: annos) {
String s = anno.annotationType().getName();
if (s.endsWith("Anno")) {
return true;
}
}
return false;
}
public List<Method> filter(List<Method> methods) {
filterCalled = true;
List<Method> forRemoval = new ArrayList<Method>();
for (Method m: methods) {
if (removeIfNotAnnotated && !isAnnotated(m)) {
forRemoval.add(m);
}
}
for (Method m: forRemoval) {
methods.remove(m);
}
return methods;
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface Anno {}
class TestObject {
public int doit(int i) {
return i;
}
@Anno
public String doit(double d) {
return "double "+d;
}
}
@Test
public void testAddingMethodResolvers() {
StandardEvaluationContext ctx = new StandardEvaluationContext();
// reflective method accessor is the only one by default
List<MethodResolver> methodResolvers = ctx.getMethodResolvers();
Assert.assertEquals(1,methodResolvers.size());
MethodResolver dummy = new DummyMethodResolver();
ctx.addMethodResolver(dummy);
Assert.assertEquals(2,ctx.getMethodResolvers().size());
List<MethodResolver> copy = new ArrayList<MethodResolver>();
copy.addAll(ctx.getMethodResolvers());
Assert.assertTrue(ctx.removeMethodResolver(dummy));
Assert.assertFalse(ctx.removeMethodResolver(dummy));
Assert.assertEquals(1,ctx.getMethodResolvers().size());
ctx.setMethodResolvers(copy);
Assert.assertEquals(2,ctx.getMethodResolvers().size());
}
static class DummyMethodResolver implements MethodResolver {
public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,
List<TypeDescriptor> argumentTypes) throws AccessException {
throw new UnsupportedOperationException("Auto-generated method stub");
}
}
@Test
public void testVarargsInvocation01() {
// Calling 'public int aVarargsMethod(String... strings)'
//evaluate("aVarargsMethod('a','b','c')", 3, Integer.class);
//evaluate("aVarargsMethod('a')", 1, Integer.class);
evaluate("aVarargsMethod()", 0, Integer.class);
evaluate("aVarargsMethod(1,2,3)", 3, Integer.class); // all need converting to strings
evaluate("aVarargsMethod(1)", 1, Integer.class); // needs string conversion
evaluate("aVarargsMethod(1,'a',3.0d)", 3, Integer.class); // first and last need conversion
// evaluate("aVarargsMethod(new String[]{'a','b','c'})", 3, Integer.class);
}
@Test
public void testVarargsInvocation02() {
// Calling 'public int aVarargsMethod2(int i, String... strings)' - returns int+length_of_strings
evaluate("aVarargsMethod2(5,'a','b','c')", 8, Integer.class);
evaluate("aVarargsMethod2(2,'a')", 3, Integer.class);
evaluate("aVarargsMethod2(4)", 4, Integer.class);
evaluate("aVarargsMethod2(8,2,3)", 10, Integer.class);
evaluate("aVarargsMethod2(9)", 9, Integer.class);
evaluate("aVarargsMethod2(2,'a',3.0d)", 4, Integer.class);
// evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", 11, Integer.class);
}
@Test
public void testInvocationOnNullContextObject() {
evaluateAndCheckError("null.toString()",SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2002-2009 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 junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.OperatorOverloader;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.standard.SpelExpression;
/**
* Test providing operator support
*
* @author Andy Clement
*/
public class OperatorOverloaderTests extends ExpressionTestCase {
static class StringAndBooleanAddition implements OperatorOverloader {
public Object operate(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException {
if (operation==Operation.ADD) {
return ((String)leftOperand)+((Boolean)rightOperand).toString();
} else {
return leftOperand;
}
}
public boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand)
throws EvaluationException {
if (leftOperand instanceof String && rightOperand instanceof Boolean) {
return true;
}
return false;
}
}
@Test
public void testSimpleOperations() throws Exception {
// no built in support for this:
evaluateAndCheckError("'abc'-true",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
eContext.setOperatorOverloader(new StringAndBooleanAddition());
SpelExpression expr = (SpelExpression)parser.parseExpression("'abc'+true");
Assert.assertEquals("abctrue",expr.getValue(eContext));
expr = (SpelExpression)parser.parseExpression("'abc'-true");
Assert.assertEquals("abc",expr.getValue(eContext));
expr = (SpelExpression)parser.parseExpression("'abc'+null");
Assert.assertEquals("abcnull",expr.getValue(eContext));
}
}

View File

@@ -0,0 +1,400 @@
/*
* Copyright 2002-2009 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 junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.spel.ast.Operator;
import org.springframework.expression.spel.standard.SpelExpression;
/**
* Tests the evaluation of expressions using relational operators.
*
* @author Andy Clement
*/
public class OperatorTests extends ExpressionTestCase {
@Test
public void testIntegerLiteral() {
evaluate("3", 3, Integer.class);
}
@Test
public void testRealLiteral() {
evaluate("3.5", 3.5d, Double.class);
}
@Test
public void testLessThan() {
evaluate("3 < 5", true, Boolean.class);
evaluate("5 < 3", false, Boolean.class);
evaluate("3L < 5L", true, Boolean.class);
evaluate("5L < 3L", false, Boolean.class);
evaluate("3.0d < 5.0d", true, Boolean.class);
evaluate("5.0d < 3.0d", false, Boolean.class);
evaluate("'abc' < 'def'",true,Boolean.class);
evaluate("'def' < 'abc'",false,Boolean.class);
evaluate("3 lt 5", true, Boolean.class);
evaluate("5 lt 3", false, Boolean.class);
evaluate("3L lt 5L", true, Boolean.class);
evaluate("5L lt 3L", false, Boolean.class);
evaluate("3.0d lT 5.0d", true, Boolean.class);
evaluate("5.0d Lt 3.0d", false, Boolean.class);
evaluate("'abc' LT 'def'",true,Boolean.class);
evaluate("'def' lt 'abc'",false,Boolean.class);
}
@Test
public void testLessThanOrEqual() {
evaluate("3 <= 5", true, Boolean.class);
evaluate("5 <= 3", false, Boolean.class);
evaluate("6 <= 6", true, Boolean.class);
evaluate("3L <= 5L", true, Boolean.class);
evaluate("5L <= 3L", false, Boolean.class);
evaluate("5L <= 5L", true, Boolean.class);
evaluate("3.0d <= 5.0d", true, Boolean.class);
evaluate("5.0d <= 3.0d", false, Boolean.class);
evaluate("5.0d <= 5.0d", true, Boolean.class);
evaluate("'abc' <= 'def'",true,Boolean.class);
evaluate("'def' <= 'abc'",false,Boolean.class);
evaluate("'abc' <= 'abc'",true,Boolean.class);
evaluate("3 le 5", true, Boolean.class);
evaluate("5 le 3", false, Boolean.class);
evaluate("6 Le 6", true, Boolean.class);
evaluate("3L lE 5L", true, Boolean.class);
evaluate("5L LE 3L", false, Boolean.class);
evaluate("5L le 5L", true, Boolean.class);
evaluate("3.0d LE 5.0d", true, Boolean.class);
evaluate("5.0d lE 3.0d", false, Boolean.class);
evaluate("5.0d Le 5.0d", true, Boolean.class);
evaluate("'abc' Le 'def'",true,Boolean.class);
evaluate("'def' LE 'abc'",false,Boolean.class);
evaluate("'abc' le 'abc'",true,Boolean.class);
}
@Test
public void testEqual() {
evaluate("3 == 5", false, Boolean.class);
evaluate("5 == 3", false, Boolean.class);
evaluate("6 == 6", true, Boolean.class);
evaluate("3.0f == 5.0f", false, Boolean.class);
evaluate("3.0f == 3.0f", true, Boolean.class);
evaluate("'abc' == null", false, Boolean.class);
evaluate("3 eq 5", false, Boolean.class);
evaluate("5 eQ 3", false, Boolean.class);
evaluate("6 Eq 6", true, Boolean.class);
evaluate("3.0f eq 5.0f", false, Boolean.class);
evaluate("3.0f EQ 3.0f", true, Boolean.class);
evaluate("'abc' EQ null", false, Boolean.class);
}
@Test
public void testNotEqual() {
evaluate("3 != 5", true, Boolean.class);
evaluate("5 != 3", true, Boolean.class);
evaluate("6 != 6", false, Boolean.class);
evaluate("3.0f != 5.0f", true, Boolean.class);
evaluate("3.0f != 3.0f", false, Boolean.class);
evaluate("3 ne 5", true, Boolean.class);
evaluate("5 nE 3", true, Boolean.class);
evaluate("6 Ne 6", false, Boolean.class);
evaluate("3.0f NE 5.0f", true, Boolean.class);
evaluate("3.0f ne 3.0f", false, Boolean.class);
}
@Test
public void testGreaterThanOrEqual() {
evaluate("3 >= 5", false, Boolean.class);
evaluate("5 >= 3", true, Boolean.class);
evaluate("6 >= 6", true, Boolean.class);
evaluate("3L >= 5L", false, Boolean.class);
evaluate("5L >= 3L", true, Boolean.class);
evaluate("5L >= 5L", true, Boolean.class);
evaluate("3.0d >= 5.0d", false, Boolean.class);
evaluate("5.0d >= 3.0d", true, Boolean.class);
evaluate("5.0d <= 5.0d", true, Boolean.class);
evaluate("'abc' >= 'def'",false,Boolean.class);
evaluate("'def' >= 'abc'",true,Boolean.class);
evaluate("'abc' >= 'abc'",true,Boolean.class);
evaluate("3 GE 5", false, Boolean.class);
evaluate("5 gE 3", true, Boolean.class);
evaluate("6 Ge 6", true, Boolean.class);
evaluate("3L ge 5L", false, Boolean.class);
}
@Test
public void testGreaterThan() {
evaluate("3 > 5", false, Boolean.class);
evaluate("5 > 3", true, Boolean.class);
evaluate("3L > 5L", false, Boolean.class);
evaluate("5L > 3L", true, Boolean.class);
evaluate("3.0d > 5.0d", false, Boolean.class);
evaluate("5.0d > 3.0d", true, Boolean.class);
evaluate("'abc' > 'def'",false,Boolean.class);
evaluate("'def' > 'abc'",true,Boolean.class);
evaluate("3.0d gt 5.0d", false, Boolean.class);
evaluate("5.0d gT 3.0d", true, Boolean.class);
evaluate("'abc' Gt 'def'",false,Boolean.class);
evaluate("'def' GT 'abc'",true,Boolean.class);
}
@Test
public void testMultiplyStringInt() {
evaluate("'a' * 5", "aaaaa", String.class);
}
@Test
public void testMultiplyDoubleDoubleGivesDouble() {
evaluate("3.0d * 5.0d", 15.0d, Double.class);
}
@Test
public void testMathOperatorAdd02() {
evaluate("'hello' + ' ' + 'world'", "hello world", String.class);
}
@Test
public void testMathOperatorsInChains() {
evaluate("1+2+3",6,Integer.class);
evaluate("2*3*4",24,Integer.class);
evaluate("12-1-2",9,Integer.class);
}
@Test
public void testIntegerArithmetic() {
evaluate("2 + 4", "6", Integer.class);
evaluate("5 - 4", "1", Integer.class);
evaluate("3 * 5", 15, Integer.class);
evaluate("3.2d * 5", 16.0d, Double.class);
evaluate("3 * 5f", 15d, Double.class);
evaluate("3 / 1", 3, Integer.class);
evaluate("3 % 2", 1, Integer.class);
evaluate("3 mod 2", 1, Integer.class);
evaluate("3 mOd 2", 1, Integer.class);
evaluate("3 Mod 2", 1, Integer.class);
evaluate("3 MOD 2", 1, Integer.class);
}
@Test
public void testPlus() throws Exception {
evaluate("7 + 2", "9", Integer.class);
evaluate("3.0f + 5.0f", 8.0d, Double.class);
evaluate("3.0d + 5.0d", 8.0d, Double.class);
evaluate("'ab' + 2", "ab2", String.class);
evaluate("2 + 'a'", "2a", String.class);
evaluate("'ab' + null", "abnull", String.class);
evaluate("null + 'ab'", "nullab", String.class);
// AST:
SpelExpression expr = (SpelExpression)parser.parseExpression("+3");
Assert.assertEquals("+3",expr.toStringAST());
expr = (SpelExpression)parser.parseExpression("2+3");
Assert.assertEquals("(2 + 3)",expr.toStringAST());
// use as a unary operator
evaluate("+5d",5d,Double.class);
evaluate("+5L",5L,Long.class);
evaluate("+5",5,Integer.class);
evaluateAndCheckError("+'abc'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
// string concatenation
evaluate("'abc'+'def'","abcdef",String.class);
//
evaluate("5 + new Integer('37')",42,Integer.class);
}
@Test
public void testMinus() throws Exception {
evaluate("'c' - 2", "a", String.class);
evaluate("3.0f - 5.0f", -2.0d, Double.class);
evaluateAndCheckError("'ab' - 2", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
evaluateAndCheckError("2-'ab'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
SpelExpression expr = (SpelExpression)parser.parseExpression("-3");
Assert.assertEquals("-3",expr.toStringAST());
expr = (SpelExpression)parser.parseExpression("2-3");
Assert.assertEquals("(2 - 3)",expr.toStringAST());
evaluate("-5d",-5d,Double.class);
evaluate("-5L",-5L,Long.class);
evaluate("-5",-5,Integer.class);
evaluateAndCheckError("-'abc'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
}
@Test
public void testModulus() {
evaluate("3%2",1,Integer.class);
evaluate("3L%2L",1L,Long.class);
evaluate("3.0f%2.0f",1d,Double.class);
evaluate("5.0d % 3.1d", 1.9d, Double.class);
evaluateAndCheckError("'abc'%'def'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
}
@Test
public void testDivide() {
evaluate("3.0f / 5.0f", 0.6d, Double.class);
evaluate("4L/2L",2L,Long.class);
evaluate("3.0f div 5.0f", 0.6d, Double.class);
evaluate("4L DIV 2L",2L,Long.class);
evaluateAndCheckError("'abc'/'def'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
}
@Test
public void testMathOperatorDivide_ConvertToDouble() {
evaluateAndAskForReturnType("8/4", new Double(2.0), Double.class);
}
@Test
public void testMathOperatorDivide04_ConvertToFloat() {
evaluateAndAskForReturnType("8/4", new Float(2.0), Float.class);
}
@Test
public void testDoubles() {
evaluate("3.0d == 5.0d", false, Boolean.class);
evaluate("3.0d == 3.0d", true, Boolean.class);
evaluate("3.0d != 5.0d", true, Boolean.class);
evaluate("3.0d != 3.0d", false, Boolean.class);
evaluate("3.0d + 5.0d", 8.0d, Double.class);
evaluate("3.0d - 5.0d", -2.0d, Double.class);
evaluate("3.0d * 5.0d", 15.0d, Double.class);
evaluate("3.0d / 5.0d", 0.6d, Double.class);
evaluate("6.0d % 3.5d", 2.5d, Double.class);
}
@Test
public void testOperatorNames() throws Exception {
Operator node = getOperatorNode((SpelExpression)parser.parseExpression("1==3"));
Assert.assertEquals("==",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("1!=3"));
Assert.assertEquals("!=",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3/3"));
Assert.assertEquals("/",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3+3"));
Assert.assertEquals("+",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3-3"));
Assert.assertEquals("-",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3<4"));
Assert.assertEquals("<",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3<=4"));
Assert.assertEquals("<=",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3*4"));
Assert.assertEquals("*",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3%4"));
Assert.assertEquals("%",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3>=4"));
Assert.assertEquals(">=",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3 between 4"));
Assert.assertEquals("between",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3 ^ 4"));
Assert.assertEquals("^",node.getOperatorName());
}
@Test
public void testOperatorOverloading() {
evaluateAndCheckError("'a' * '2'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
evaluateAndCheckError("'a' ^ '2'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
}
@Test
public void testPower() {
evaluate("3^2",9,Integer.class);
evaluate("3.0d^2.0d",9.0d,Double.class);
evaluate("3L^2L",9L,Long.class);
evaluate("(2^32)^2",9223372036854775807L,Long.class);
}
@Test
public void testMixedOperands_FloatsAndDoubles() {
evaluate("3.0d + 5.0f", 8.0d, Double.class);
evaluate("3.0D - 5.0f", -2.0d, Double.class);
evaluate("3.0f * 5.0d", 15.0d, Double.class);
evaluate("3.0f / 5.0D", 0.6d, Double.class);
evaluate("5.0D % 3f", 2.0d, Double.class);
}
@Test
public void testMixedOperands_DoublesAndInts() {
evaluate("3.0d + 5", 8.0d, Double.class);
evaluate("3.0D - 5", -2.0d, Double.class);
evaluate("3.0f * 5", 15.0d, Double.class);
evaluate("6.0f / 2", 3.0, Double.class);
evaluate("6.0f / 4", 1.5d, Double.class);
evaluate("5.0D % 3", 2.0d, Double.class);
evaluate("5.5D % 3", 2.5, Double.class);
}
@Test
public void testStrings() {
evaluate("'abc' == 'abc'",true,Boolean.class);
evaluate("'abc' == 'def'",false,Boolean.class);
evaluate("'abc' != 'abc'",false,Boolean.class);
evaluate("'abc' != 'def'",true,Boolean.class);
}
@Test
public void testLongs() {
evaluate("3L == 4L", false, Boolean.class);
evaluate("3L == 3L", true, Boolean.class);
evaluate("3L != 4L", true, Boolean.class);
evaluate("3L != 3L", false, Boolean.class);
evaluate("3L * 50L", 150L, Long.class);
evaluate("3L + 50L", 53L, Long.class);
evaluate("3L - 50L", -47L, Long.class);
}
// ---
private Operator getOperatorNode(SpelExpression e) {
SpelNode node = e.getAST();
return (Operator)findNode(node,Operator.class);
}
private SpelNode findNode(SpelNode node, Class<Operator> clazz) {
if (clazz.isAssignableFrom(node.getClass())) {
return node;
}
int childCount = node.getChildCount();
for (int i=0;i<childCount;i++) {
SpelNode possible = findNode(node.getChild(i),clazz);
if (possible!=null) {
return possible;
}
}
return null;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2002-2009 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 org.junit.Test;
/**
* Tests the messages and exceptions that come out for badly formed expressions
*
* @author Andy Clement
*/
public class ParserErrorMessagesTests extends ExpressionTestCase {
@Test
public void testBrokenExpression01() {
// will not fit into an int, needs L suffix
parseAndCheckError("0xCAFEBABE", SpelMessage.NOT_AN_INTEGER);
evaluate("0xCAFEBABEL", 0xCAFEBABEL, Long.class);
parseAndCheckError("0xCAFEBABECAFEBABEL", SpelMessage.NOT_A_LONG);
}
@Test
public void testBrokenExpression02() {
// rogue 'G' on the end
parseAndCheckError("0xB0BG", SpelMessage.MORE_INPUT, 5, "G");
}
@Test
public void testBrokenExpression04() {
// missing right operand
parseAndCheckError("true or ", SpelMessage.RIGHT_OPERAND_PROBLEM, 5);
}
@Test
public void testBrokenExpression05() {
// missing right operand
parseAndCheckError("1 + ", SpelMessage.RIGHT_OPERAND_PROBLEM, 2);
}
@Test
public void testBrokenExpression07() {
// T() can only take an identifier (possibly qualified), not a literal
// message ought to say identifier rather than ID
parseAndCheckError("null instanceof T('a')", SpelMessage.NOT_EXPECTED_TOKEN, 18,
"identifier","literal_string");
}
}

View File

@@ -0,0 +1,474 @@
/*
* Copyright 2002-2009 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 junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* Parse some expressions and check we get the AST we expect. Rather than inspecting each node in the AST, we ask it to
* write itself to a string form and check that is as expected.
*
* @author Andy Clement
*/
public class ParsingTests {
private SpelExpressionParser parser = new SpelExpressionParser();
// literals
@Test
public void testLiteralBoolean01() {
parseCheck("false");
}
@Test
public void testLiteralLong01() {
parseCheck("37L", "37");
}
@Test
public void testLiteralBoolean02() {
parseCheck("true");
}
@Test
public void testLiteralBoolean03() {
parseCheck("!true");
}
@Test
public void testLiteralInteger01() {
parseCheck("1");
}
@Test
public void testLiteralInteger02() {
parseCheck("1415");
}
@Test
public void testLiteralString01() {
parseCheck("'hello'");
}
@Test
public void testLiteralString02() {
parseCheck("'joe bloggs'");
}
@Test
public void testLiteralString03() {
parseCheck("'Tony''s Pizza'", "'Tony's Pizza'");
}
@Test
public void testLiteralReal01() {
parseCheck("6.0221415E+23", "6.0221415E23");
}
@Test
public void testLiteralHex01() {
parseCheck("0x7FFFFFFF", "2147483647");
}
@Test
public void testLiteralDate01() {
parseCheck("date('1974/08/24')");
}
@Test
public void testLiteralDate02() {
parseCheck("date('19740824T131030','yyyyMMddTHHmmss')");
}
@Test
public void testLiteralNull01() {
parseCheck("null");
}
// boolean operators
@Test
public void testBooleanOperatorsOr01() {
parseCheck("false or false", "(false or false)");
}
@Test
public void testBooleanOperatorsOr02() {
parseCheck("false or true", "(false or true)");
}
@Test
public void testBooleanOperatorsOr03() {
parseCheck("true or false", "(true or false)");
}
@Test
public void testBooleanOperatorsOr04() {
parseCheck("true or false", "(true or false)");
}
@Test
public void testBooleanOperatorsMix01() {
parseCheck("false or true and false", "(false or (true and false))");
}
// relational operators
@Test
public void testRelOperatorsGT01() {
parseCheck("3>6", "(3 > 6)");
}
@Test
public void testRelOperatorsLT01() {
parseCheck("3<6", "(3 < 6)");
}
@Test
public void testRelOperatorsLE01() {
parseCheck("3<=6", "(3 <= 6)");
}
@Test
public void testRelOperatorsGE01() {
parseCheck("3>=6", "(3 >= 6)");
}
@Test
public void testRelOperatorsGE02() {
parseCheck("3>=3", "(3 >= 3)");
}
@Test
public void testElvis() {
parseCheck("3?:1", "3 ?: 1");
}
// public void testRelOperatorsIn01() {
// parseCheck("3 in {1,2,3,4,5}", "(3 in {1,2,3,4,5})");
// }
//
// public void testRelOperatorsBetween01() {
// parseCheck("1 between {1, 5}", "(1 between {1,5})");
// }
// public void testRelOperatorsBetween02() {
// parseCheck("'efg' between {'abc', 'xyz'}", "('efg' between {'abc','xyz'})");
// }// true
@Test
public void testRelOperatorsIs01() {
parseCheck("'xyz' instanceof int", "('xyz' instanceof int)");
}// false
// public void testRelOperatorsIs02() {
// parseCheck("{1, 2, 3, 4, 5} instanceof List", "({1,2,3,4,5} instanceof List)");
// }// true
@Test
public void testRelOperatorsMatches01() {
parseCheck("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "('5.0067' matches '^-?\\d+(\\.\\d{2})?$')");
}// false
@Test
public void testRelOperatorsMatches02() {
parseCheck("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "('5.00' matches '^-?\\d+(\\.\\d{2})?$')");
}// true
// mathematical operators
@Test
public void testMathOperatorsAdd01() {
parseCheck("2+4", "(2 + 4)");
}
@Test
public void testMathOperatorsAdd02() {
parseCheck("'a'+'b'", "('a' + 'b')");
}
@Test
public void testMathOperatorsAdd03() {
parseCheck("'hello'+' '+'world'", "(('hello' + ' ') + 'world')");
}
@Test
public void testMathOperatorsSubtract01() {
parseCheck("5-4", "(5 - 4)");
}
@Test
public void testMathOperatorsMultiply01() {
parseCheck("7*4", "(7 * 4)");
}
@Test
public void testMathOperatorsDivide01() {
parseCheck("8/4", "(8 / 4)");
}
@Test
public void testMathOperatorModulus01() {
parseCheck("7 % 4", "(7 % 4)");
}
// mixed operators
@Test
public void testMixedOperators01() {
parseCheck("true and 5>3", "(true and (5 > 3))");
}
// collection processors
// public void testCollectionProcessorsCount01() {
// parseCheck("new String[] {'abc','def','xyz'}.count()");
// }
// public void testCollectionProcessorsCount02() {
// parseCheck("new int[] {1,2,3}.count()");
// }
//
// public void testCollectionProcessorsMax01() {
// parseCheck("new int[] {1,2,3}.max()");
// }
//
// public void testCollectionProcessorsMin01() {
// parseCheck("new int[] {1,2,3}.min()");
// }
//
// public void testCollectionProcessorsAverage01() {
// parseCheck("new int[] {1,2,3}.average()");
// }
//
// public void testCollectionProcessorsSort01() {
// parseCheck("new int[] {3,2,1}.sort()");
// }
//
// public void testCollectionProcessorsNonNull01() {
// parseCheck("{'a','b',null,'d',null}.nonNull()");
// }
//
// public void testCollectionProcessorsDistinct01() {
// parseCheck("{'a','b','a','d','e'}.distinct()");
// }
// references
@Test
public void testReferences01() {
parseCheck("@foo");
parseCheck("@'foo.bar'");
parseCheck("@\"foo.bar.goo\"","@'foo.bar.goo'");
}
@Test
public void testReferences03() {
parseCheck("@$$foo");
}
// properties
@Test
public void testProperties01() {
parseCheck("name");
}
@Test
public void testProperties02() {
parseCheck("placeofbirth.CitY");
}
@Test
public void testProperties03() {
parseCheck("a.b.c.d.e");
}
// inline list creation
// public void testInlineListCreation01() {
// parseCheck("{1, 2, 3, 4, 5}", "{1,2,3,4,5}");
// }
//
// public void testInlineListCreation02() {
// parseCheck("{'abc','xyz'}", "{'abc','xyz'}");
// }
// // inline map creation
// public void testInlineMapCreation01() {
// parseCheck("#{'key1':'Value 1', 'today':DateTime.Today}");
// }
//
// public void testInlineMapCreation02() {
// parseCheck("#{1:'January', 2:'February', 3:'March'}");
// }
//
// public void testInlineMapCreation03() {
// parseCheck("#{'key1':'Value 1', 'today':'Monday'}['key1']");
// }
//
// public void testInlineMapCreation04() {
// parseCheck("#{1:'January', 2:'February', 3:'March'}[3]");
// }
// methods
@Test
public void testMethods01() {
parseCheck("echo(12)");
}
@Test
public void testMethods02() {
parseCheck("echo(name)");
}
@Test
public void testMethods03() {
parseCheck("age.doubleItAndAdd(12)");
}
// constructors
@Test
public void testConstructors01() {
parseCheck("new String('hello')");
}
// public void testConstructors02() {
// parseCheck("new String[3]");
// }
// array construction
// public void testArrayConstruction01() {
// parseCheck("new int[] {1, 2, 3, 4, 5}", "new int[] {1,2,3,4,5}");
// }
//
// public void testArrayConstruction02() {
// parseCheck("new String[] {'abc','xyz'}", "new String[] {'abc','xyz'}");
// }
// variables and functions
@Test
public void testVariables01() {
parseCheck("#foo");
}
@Test
public void testFunctions01() {
parseCheck("#fn(1,2,3)");
}
@Test
public void testFunctions02() {
parseCheck("#fn('hello')");
}
// projections and selections
// public void testProjections01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.!{#isEven()}");
// }
// public void testSelections01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'}",
// "{1,2,3,4,5,6,7,8,9,10}.?{(#isEven(#this) == 'y')}");
// }
// public void testSelectionsFirst01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.^{#isEven(#this) == 'y'}",
// "{1,2,3,4,5,6,7,8,9,10}.^{(#isEven(#this) == 'y')}");
// }
// public void testSelectionsLast01() {
// parseCheck("{1,2,3,4,5,6,7,8,9,10}.${#isEven(#this) == 'y'}",
// "{1,2,3,4,5,6,7,8,9,10}.${(#isEven(#this) == 'y')}");
// }
// assignment
@Test
public void testAssignmentToVariables01() {
parseCheck("#var1='value1'");
}
// ternary operator
@Test
public void testTernaryOperator01() {
parseCheck("1>2?3:4","(1 > 2) ? 3 : 4");
}
// public void testTernaryOperator01() {
// parseCheck("{1}.#isEven(#this) == 'y'?'it is even':'it is odd'",
// "({1}.#isEven(#this) == 'y') ? 'it is even' : 'it is odd'");
// }
//
// public void testLambdaMax() {
// parseCheck("(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", "(#max={|x,y| ($x > $y) ? $x : $y };#max(5,25))");
// }
//
// public void testLambdaFactorial() {
// parseCheck("(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))",
// "(#fact={|n| ($n <= 1) ? 1 : ($n * #fact(($n - 1))) };#fact(5))");
// } // 120
// Type references
@Test
public void testTypeReferences01() {
parseCheck("T(java.lang.String)");
}
@Test
public void testTypeReferences02() {
parseCheck("T(String)");
}
@Test
public void testInlineList1() {
parseCheck("{1,2,3,4}");
}
/**
* Parse the supplied expression and then create a string representation of the resultant AST, it should be the same
* as the original expression.
*
* @param expression the expression to parse *and* the expected value of the string form of the resultant AST
*/
public void parseCheck(String expression) {
parseCheck(expression, expression);
}
/**
* Parse the supplied expression and then create a string representation of the resultant AST, it should be the
* expected value.
*
* @param expression the expression to parse
* @param expectedStringFormOfAST the expected string form of the AST
*/
public void parseCheck(String expression, String expectedStringFormOfAST) {
try {
SpelExpression e = (SpelExpression) parser.parseRaw(expression);
if (e != null && !e.toStringAST().equals(expectedStringFormOfAST)) {
SpelUtilities.printAbstractSyntaxTree(System.err, e);
}
if (e == null) {
Assert.fail("Parsed exception was null");
}
Assert.assertEquals("String form of AST does not match expected output", expectedStringFormOfAST, e.toStringAST());
} catch (ParseException ee) {
ee.printStackTrace();
Assert.fail("Unexpected Exception: " + ee.getMessage());
}
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2002-2009 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 junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
///CLOVER:OFF
/**
* Tests the evaluation of real expressions in a real context.
*
* @author Andy Clement
*/
public class PerformanceTests {
public static final int ITERATIONS = 10000;
public static final boolean report = true;
private static ExpressionParser parser = new SpelExpressionParser();
private static EvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
private static final boolean DEBUG = false;
@Test
public void testPerformanceOfPropertyAccess() throws Exception {
long starttime = 0;
long endtime = 0;
// warmup
for (int i = 0; i < ITERATIONS; i++) {
Expression expr = parser.parseExpression("placeOfBirth.city");
if (expr == null) {
Assert.fail("Parser returned null for expression");
}
expr.getValue(eContext);
}
starttime = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
Expression expr = parser.parseExpression("placeOfBirth.city");
if (expr == null) {
Assert.fail("Parser returned null for expression");
}
expr.getValue(eContext);
}
endtime = System.currentTimeMillis();
long freshParseTime = endtime - starttime;
if (DEBUG) {
System.out.println("PropertyAccess: Time for parsing and evaluation x 10000: "+freshParseTime+"ms");
}
Expression expr = parser.parseExpression("placeOfBirth.city");
if (expr == null) {
Assert.fail("Parser returned null for expression");
}
starttime = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
expr.getValue(eContext);
}
endtime = System.currentTimeMillis();
long reuseTime = endtime - starttime;
if (DEBUG) {
System.out.println("PropertyAccess: Time for just evaluation x 10000: "+reuseTime+"ms");
}
if (reuseTime > freshParseTime) {
System.out.println("Fresh parse every time, ITERATIONS iterations = " + freshParseTime + "ms");
System.out.println("Reuse SpelExpression, ITERATIONS iterations = " + reuseTime + "ms");
Assert.fail("Should have been quicker to reuse!");
}
}
public void testPerformanceOfMethodAccess() throws Exception {
long starttime = 0;
long endtime = 0;
// warmup
for (int i = 0; i < ITERATIONS; i++) {
Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()");
if (expr == null) {
Assert.fail("Parser returned null for expression");
}
expr.getValue(eContext);
}
starttime = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()");
if (expr == null) {
Assert.fail("Parser returned null for expression");
}
expr.getValue(eContext);
}
endtime = System.currentTimeMillis();
long freshParseTime = endtime - starttime;
if (DEBUG) {
System.out.println("MethodExpression: Time for parsing and evaluation x 10000: "+freshParseTime+"ms");
}
Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()");
if (expr == null) {
Assert.fail("Parser returned null for expression");
}
starttime = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
expr.getValue(eContext);
}
endtime = System.currentTimeMillis();
long reuseTime = endtime - starttime;
if (DEBUG) {
System.out.println("MethodExpression: Time for just evaluation x 10000: "+reuseTime+"ms");
}
if (reuseTime > freshParseTime) {
System.out.println("Fresh parse every time, ITERATIONS iterations = " + freshParseTime + "ms");
System.out.println("Reuse SpelExpression, ITERATIONS iterations = " + reuseTime + "ms");
Assert.fail("Should have been quicker to reuse!");
}
}
}

View File

@@ -0,0 +1,198 @@
/*
* Copyright 2002-2009 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.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
///CLOVER:OFF
/**
* Tests accessing of properties.
*
* @author Andy Clement
*/
public class PropertyAccessTests extends ExpressionTestCase {
@Test
public void testSimpleAccess01() {
evaluate("name", "Nikola Tesla", String.class);
}
@Test
public void testSimpleAccess02() {
evaluate("placeOfBirth.city", "SmilJan", String.class);
}
@Test
public void testSimpleAccess03() {
evaluate("stringArrayOfThreeItems.length", "3", Integer.class);
}
@Test
public void testNonExistentPropertiesAndMethods() {
// madeup does not exist as a property
evaluateAndCheckError("madeup", SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, 0);
// name is ok but foobar does not exist:
evaluateAndCheckError("name.foobar", SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, 5);
}
/**
* The standard reflection resolver cannot find properties on null objects but some
* supplied resolver might be able to - so null shouldn't crash the reflection resolver.
*/
@Test
public void testAccessingOnNullObject() throws Exception {
SpelExpression expr = (SpelExpression)parser.parseExpression("madeup");
EvaluationContext context = new StandardEvaluationContext(null);
try {
expr.getValue(context);
Assert.fail("Should have failed - default property resolver cannot resolve on null");
} catch (Exception e) {
checkException(e,SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL);
}
Assert.assertFalse(expr.isWritable(context));
try {
expr.setValue(context,"abc");
Assert.fail("Should have failed - default property resolver cannot resolve on null");
} catch (Exception e) {
checkException(e,SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL);
}
}
private void checkException(Exception e, SpelMessage expectedMessage) {
if (e instanceof SpelEvaluationException) {
SpelMessage sm = ((SpelEvaluationException)e).getMessageCode();
Assert.assertEquals("Expected exception type did not occur",expectedMessage,sm);
} else {
Assert.fail("Should be a SpelException "+e);
}
}
@Test
// Adding a new property accessor just for a particular type
public void testAddingSpecificPropertyAccessor() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = new StandardEvaluationContext();
// Even though this property accessor is added after the reflection one, it specifically
// names the String class as the type it is interested in so is chosen in preference to
// any 'default' ones
ctx.addPropertyAccessor(new StringyPropertyAccessor());
Expression expr = parser.parseRaw("new String('hello').flibbles");
Integer i = expr.getValue(ctx, Integer.class);
Assert.assertEquals((int) i, 7);
// The reflection one will be used for other properties...
expr = parser.parseRaw("new String('hello').CASE_INSENSITIVE_ORDER");
Object o = expr.getValue(ctx);
Assert.assertNotNull(o);
expr = parser.parseRaw("new String('hello').flibbles");
expr.setValue(ctx, 99);
i = expr.getValue(ctx, Integer.class);
Assert.assertEquals((int) i, 99);
// Cannot set it to a string value
try {
expr.setValue(ctx, "not allowed");
Assert.fail("Should not have been allowed");
} catch (EvaluationException e) {
// success - message will be: EL1063E:(pos 20): A problem occurred whilst attempting to set the property
// 'flibbles': 'Cannot set flibbles to an object of type 'class java.lang.String''
// System.out.println(e.getMessage());
}
}
@Test
public void testAddingRemovingAccessors() {
StandardEvaluationContext ctx = new StandardEvaluationContext();
// reflective property accessor is the only one by default
List<PropertyAccessor> propertyAccessors = ctx.getPropertyAccessors();
Assert.assertEquals(1,propertyAccessors.size());
StringyPropertyAccessor spa = new StringyPropertyAccessor();
ctx.addPropertyAccessor(spa);
Assert.assertEquals(2,ctx.getPropertyAccessors().size());
List<PropertyAccessor> copy = new ArrayList<PropertyAccessor>();
copy.addAll(ctx.getPropertyAccessors());
Assert.assertTrue(ctx.removePropertyAccessor(spa));
Assert.assertFalse(ctx.removePropertyAccessor(spa));
Assert.assertEquals(1,ctx.getPropertyAccessors().size());
ctx.setPropertyAccessors(copy);
Assert.assertEquals(2,ctx.getPropertyAccessors().size());
}
// This can resolve the property 'flibbles' on any String (very useful...)
private static class StringyPropertyAccessor implements PropertyAccessor {
int flibbles = 7;
public Class<?>[] getSpecificTargetClasses() {
return new Class[] { String.class };
}
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
if (!(target instanceof String))
throw new RuntimeException("Assertion Failed! target should be String");
return (name.equals("flibbles"));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
if (!(target instanceof String))
throw new RuntimeException("Assertion Failed! target should be String");
return (name.equals("flibbles"));
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
if (!name.equals("flibbles"))
throw new RuntimeException("Assertion Failed! name should be flibbles");
return new TypedValue(flibbles);
}
public void write(EvaluationContext context, Object target, String name, Object newValue)
throws AccessException {
if (!name.equals("flibbles"))
throw new RuntimeException("Assertion Failed! name should be flibbles");
try {
flibbles = (Integer) context.getTypeConverter().convertValue(newValue, TypeDescriptor.forObject(newValue), TypeDescriptor.valueOf(Integer.class));
}catch (EvaluationException e) {
throw new AccessException("Cannot set flibbles to an object of type '" + newValue.getClass() + "'");
}
}
}
}

View File

@@ -0,0 +1,315 @@
/*
* Copyright 2002-2010 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.lang.reflect.Method;
import java.util.List;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.ReflectionHelper;
import org.springframework.expression.spel.support.StandardEvaluationContext;
///CLOVER:OFF
/**
* Spring Security scenarios from https://wiki.springsource.com/display/SECURITY/Spring+Security+Expression-based+Authorization
*
* @author Andy Clement
*/
public class ScenariosForSpringSecurity extends ExpressionTestCase {
@Test
public void testScenario01_Roles() throws Exception {
try {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = new StandardEvaluationContext();
Expression expr = parser.parseRaw("hasAnyRole('MANAGER','TELLER')");
ctx.setRootObject(new Person("Ben"));
Boolean value = expr.getValue(ctx,Boolean.class);
Assert.assertFalse(value);
ctx.setRootObject(new Manager("Luke"));
value = expr.getValue(ctx,Boolean.class);
Assert.assertTrue(value);
} catch (EvaluationException ee) {
ee.printStackTrace();
Assert.fail("Unexpected SpelException: " + ee.getMessage());
}
}
@Test
public void testScenario02_ComparingNames() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.addPropertyAccessor(new SecurityPrincipalAccessor());
// Multiple options for supporting this expression: "p.name == principal.name"
// (1) If the right person is the root context object then "name==principal.name" is good enough
Expression expr = parser.parseRaw("name == principal.name");
ctx.setRootObject(new Person("Andy"));
Boolean value = expr.getValue(ctx,Boolean.class);
Assert.assertTrue(value);
ctx.setRootObject(new Person("Christian"));
value = expr.getValue(ctx,Boolean.class);
Assert.assertFalse(value);
// (2) Or register an accessor that can understand 'p' and return the right person
expr = parser.parseRaw("p.name == principal.name");
PersonAccessor pAccessor = new PersonAccessor();
ctx.addPropertyAccessor(pAccessor);
ctx.setRootObject(null);
pAccessor.setPerson(new Person("Andy"));
value = expr.getValue(ctx,Boolean.class);
Assert.assertTrue(value);
pAccessor.setPerson(new Person("Christian"));
value = expr.getValue(ctx,Boolean.class);
Assert.assertFalse(value);
}
@Test
public void testScenario03_Arithmetic() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = new StandardEvaluationContext();
// Might be better with a as a variable although it would work as a property too...
// Variable references using a '#'
Expression expr = parser.parseRaw("(hasRole('SUPERVISOR') or (#a < 1.042)) and hasIpAddress('10.10.0.0/16')");
Boolean value = null;
ctx.setVariable("a",1.0d); // referenced as #a in the expression
ctx.setRootObject(new Supervisor("Ben")); // so non-qualified references 'hasRole()' 'hasIpAddress()' are invoked against it
value = expr.getValue(ctx,Boolean.class);
Assert.assertTrue(value);
ctx.setRootObject(new Manager("Luke"));
ctx.setVariable("a",1.043d);
value = expr.getValue(ctx,Boolean.class);
Assert.assertFalse(value);
}
// Here i'm going to change which hasRole() executes and make it one of my own Java methods
@Test
public void testScenario04_ControllingWhichMethodsRun() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.setRootObject(new Supervisor("Ben")); // so non-qualified references 'hasRole()' 'hasIpAddress()' are invoked against it);
ctx.addMethodResolver(new MyMethodResolver()); // NEEDS TO OVERRIDE THE REFLECTION ONE - SHOW REORDERING MECHANISM
// Might be better with a as a variable although it would work as a property too...
// Variable references using a '#'
// SpelExpression expr = parser.parseExpression("(hasRole('SUPERVISOR') or (#a < 1.042)) and hasIpAddress('10.10.0.0/16')");
Expression expr = parser.parseRaw("(hasRole(3) or (#a < 1.042)) and hasIpAddress('10.10.0.0/16')");
Boolean value = null;
ctx.setVariable("a",1.0d); // referenced as #a in the expression
value = expr.getValue(ctx,Boolean.class);
Assert.assertTrue(value);
// ctx.setRootObject(new Manager("Luke"));
// ctx.setVariable("a",1.043d);
// value = (Boolean)expr.getValue(ctx,Boolean.class);
// assertFalse(value);
}
static class Person {
private String n;
Person(String n) { this.n = n; }
public String[] getRoles() { return new String[]{"NONE"}; }
public boolean hasAnyRole(String... roles) {
if (roles==null) return true;
String[] myRoles = getRoles();
for (int i=0;i<myRoles.length;i++) {
for (int j=0;j<roles.length;j++) {
if (myRoles[i].equals(roles[j])) return true;
}
}
return false;
}
public boolean hasRole(String role) {
return hasAnyRole(role);
}
public boolean hasIpAddress(String ipaddr) {
return true;
}
public String getName() { return n; }
}
static class Manager extends Person {
Manager(String n) {
super(n);
}
public String[] getRoles() { return new String[]{"MANAGER"};}
}
static class Teller extends Person {
Teller(String n) {
super(n);
}
public String[] getRoles() { return new String[]{"TELLER"};}
}
static class Supervisor extends Person {
Supervisor(String n) {
super(n);
}
public String[] getRoles() { return new String[]{"SUPERVISOR"};}
}
static class SecurityPrincipalAccessor implements PropertyAccessor {
static class Principal {
public String name = "Andy";
}
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
return name.equals("principal");
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(new Principal());
}
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 {
}
public Class<?>[] getSpecificTargetClasses() {
return null;
}
}
static class PersonAccessor implements PropertyAccessor {
Person activePerson;
void setPerson(Person p) { this.activePerson = p; }
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
return name.equals("p");
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(activePerson);
}
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 {
}
public Class<?>[] getSpecificTargetClasses() {
return null;
}
}
static class MyMethodResolver implements MethodResolver {
static class HasRoleExecutor implements MethodExecutor {
TypeConverter tc;
public HasRoleExecutor(TypeConverter typeConverter) {
this.tc = typeConverter;
}
public TypedValue execute(EvaluationContext context, Object target, Object... arguments)
throws AccessException {
try {
Method m = HasRoleExecutor.class.getMethod("hasRole", String[].class);
Object[] args = arguments;
if (args != null) {
ReflectionHelper.convertAllArguments(tc, args, m);
}
if (m.isVarArgs()) {
args = ReflectionHelper.setupArgumentsForVarargsInvocation(m.getParameterTypes(), args);
}
return new TypedValue(m.invoke(null, args), new TypeDescriptor(new MethodParameter(m,-1)));
}
catch (Exception ex) {
throw new AccessException("Problem invoking hasRole", ex);
}
}
public static boolean hasRole(String... strings) {
return true;
}
}
public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, List<TypeDescriptor> arguments)
throws AccessException {
if (name.equals("hasRole")) {
return new HasRoleExecutor(context.getTypeConverter());
}
return null;
}
}
}

View File

@@ -0,0 +1,385 @@
/*
* Copyright 2002-2010 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* @author Mark Fisher
* @author Sam Brannen
*/
public class SelectionAndProjectionTests {
@Test
public void selectionWithList() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ListTestBean());
Object value = expression.getValue(context);
assertTrue(value instanceof List);
List list = (List) value;
assertEquals(5, list.size());
assertEquals(0, list.get(0));
assertEquals(1, list.get(1));
assertEquals(2, list.get(2));
assertEquals(3, list.get(3));
assertEquals(4, list.get(4));
}
@Test
public void selectFirstItemInList() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ListTestBean());
Object value = expression.getValue(context);
assertTrue(value instanceof Integer);
assertEquals(0, value);
}
@Test
public void selectLastItemInList() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ListTestBean());
Object value = expression.getValue(context);
assertTrue(value instanceof Integer);
assertEquals(4, value);
}
@Test
public void selectionWithSet() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new SetTestBean());
Object value = expression.getValue(context);
assertTrue(value instanceof List);
List list = (List) value;
assertEquals(5, list.size());
assertEquals(0, list.get(0));
assertEquals(1, list.get(1));
assertEquals(2, list.get(2));
assertEquals(3, list.get(3));
assertEquals(4, list.get(4));
}
@Test
public void selectFirstItemInSet() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new SetTestBean());
Object value = expression.getValue(context);
assertTrue(value instanceof Integer);
assertEquals(0, value);
}
@Test
public void selectLastItemInSet() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new SetTestBean());
Object value = expression.getValue(context);
assertTrue(value instanceof Integer);
assertEquals(4, value);
}
@Test
public void selectionWithArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
assertTrue(value.getClass().isArray());
TypedValue typedValue = new TypedValue(value);
assertEquals(Integer.class, typedValue.getTypeDescriptor().getElementTypeDescriptor().getType());
Integer[] array = (Integer[]) value;
assertEquals(5, array.length);
assertEquals(new Integer(0), array[0]);
assertEquals(new Integer(1), array[1]);
assertEquals(new Integer(2), array[2]);
assertEquals(new Integer(3), array[3]);
assertEquals(new Integer(4), array[4]);
}
@Test
public void selectFirstItemInArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
assertTrue(value instanceof Integer);
assertEquals(0, value);
}
@Test
public void selectLastItemInArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
assertTrue(value instanceof Integer);
assertEquals(4, value);
}
@Test
public void selectionWithPrimitiveArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("ints.?[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
assertTrue(value.getClass().isArray());
TypedValue typedValue = new TypedValue(value);
assertEquals(Integer.class, typedValue.getTypeDescriptor().getElementTypeDescriptor().getType());
Integer[] array = (Integer[]) value;
assertEquals(5, array.length);
assertEquals(new Integer(0), array[0]);
assertEquals(new Integer(1), array[1]);
assertEquals(new Integer(2), array[2]);
assertEquals(new Integer(3), array[3]);
assertEquals(new Integer(4), array[4]);
}
@Test
public void selectFirstItemInPrimitiveArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("ints.^[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
assertTrue(value instanceof Integer);
assertEquals(0, value);
}
@Test
public void selectLastItemInPrimitiveArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("ints.$[#this<5]");
EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean());
Object value = expression.getValue(context);
assertTrue(value instanceof Integer);
assertEquals(4, value);
}
@Test
@SuppressWarnings("unchecked")
public void selectionWithMap() {
EvaluationContext context = new StandardEvaluationContext(new MapTestBean());
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("colors.?[key.startsWith('b')]");
Map<String, String> colorsMap = (Map<String, String>) exp.getValue(context);
assertEquals(3, colorsMap.size());
assertTrue(colorsMap.containsKey("beige"));
assertTrue(colorsMap.containsKey("blue"));
assertTrue(colorsMap.containsKey("brown"));
}
@Test
@SuppressWarnings("unchecked")
public void selectFirstItemInMap() {
EvaluationContext context = new StandardEvaluationContext(new MapTestBean());
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("colors.^[key.startsWith('b')]");
Map<String, String> colorsMap = (Map<String, String>) exp.getValue(context);
assertEquals(1, colorsMap.size());
assertEquals("beige", colorsMap.keySet().iterator().next());
}
@Test
@SuppressWarnings("unchecked")
public void selectLastItemInMap() {
EvaluationContext context = new StandardEvaluationContext(new MapTestBean());
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("colors.$[key.startsWith('b')]");
Map<String, String> colorsMap = (Map<String, String>) exp.getValue(context);
assertEquals(1, colorsMap.size());
assertEquals("brown", colorsMap.keySet().iterator().next());
}
@Test
public void projectionWithList() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("testList", IntegerTestBean.createList());
Object value = expression.getValue(context);
assertTrue(value instanceof List);
List list = (List) value;
assertEquals(3, list.size());
assertEquals(5, list.get(0));
assertEquals(6, list.get(1));
assertEquals(7, list.get(2));
}
@Test
public void projectionWithSet() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("testList", IntegerTestBean.createSet());
Object value = expression.getValue(context);
assertTrue(value instanceof List);
List list = (List) value;
assertEquals(3, list.size());
assertEquals(5, list.get(0));
assertEquals(6, list.get(1));
assertEquals(7, list.get(2));
}
@Test
public void projectionWithArray() throws Exception {
Expression expression = new SpelExpressionParser().parseRaw("#testArray.![wrapper.value]");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("testArray", IntegerTestBean.createArray());
Object value = expression.getValue(context);
assertTrue(value.getClass().isArray());
TypedValue typedValue = new TypedValue(value);
assertEquals(Number.class, typedValue.getTypeDescriptor().getElementTypeDescriptor().getType());
Number[] array = (Number[]) value;
assertEquals(3, array.length);
assertEquals(new Integer(5), array[0]);
assertEquals(5.9f, array[1]);
assertEquals(new Integer(7), array[2]);
}
static class MapTestBean {
private final Map<String, String> colors = new TreeMap<String, String>();
MapTestBean() {
// colors.put("black", "schwarz");
colors.put("red", "rot");
colors.put("brown", "braun");
colors.put("blue", "blau");
colors.put("yellow", "gelb");
colors.put("beige", "beige");
}
public Map<String, String> getColors() {
return colors;
}
}
static class ListTestBean {
private final List<Integer> integers = new ArrayList<Integer>();
ListTestBean() {
for (int i = 0; i < 10; i++) {
integers.add(i);
}
}
public List<Integer> getIntegers() {
return integers;
}
}
static class SetTestBean {
private final Set<Integer> integers = new LinkedHashSet<Integer>();
SetTestBean() {
for (int i = 0; i < 10; i++) {
integers.add(i);
}
}
public Set<Integer> getIntegers() {
return integers;
}
}
static class ArrayTestBean {
private final int[] ints = new int[10];
private final Integer[] integers = new Integer[10];
ArrayTestBean() {
for (int i = 0; i < 10; i++) {
ints[i] = i;
integers[i] = i;
}
}
public int[] getInts() {
return ints;
}
public Integer[] getIntegers() {
return integers;
}
}
static class IntegerTestBean {
private final IntegerWrapper wrapper;
IntegerTestBean(Number value) {
this.wrapper = new IntegerWrapper(value);
}
public IntegerWrapper getWrapper() {
return this.wrapper;
}
static List<IntegerTestBean> createList() {
List<IntegerTestBean> list = new ArrayList<IntegerTestBean>();
for (int i = 0; i < 3; i++) {
list.add(new IntegerTestBean(i + 5));
}
return list;
}
static Set<IntegerTestBean> createSet() {
Set<IntegerTestBean> set = new LinkedHashSet<IntegerTestBean>();
for (int i = 0; i < 3; i++) {
set.add(new IntegerTestBean(i + 5));
}
return set;
}
static IntegerTestBean[] createArray() {
IntegerTestBean[] array = new IntegerTestBean[3];
for (int i = 0; i < 3; i++) {
if (i == 1) {
array[i] = new IntegerTestBean(5.9f);
} else {
array[i] = new IntegerTestBean(i + 5);
}
}
return array;
}
}
static class IntegerWrapper {
private final Number value;
IntegerWrapper(Number value) {
this.value = value;
}
public Number getValue() {
return this.value;
}
}
}

View File

@@ -0,0 +1,262 @@
/*
* Copyright 2002-2009 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.util.Collection;
import java.util.Set;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.testresources.PlaceOfBirth;
/**
* Tests set value expressions.
*
* @author Keith Donald
* @author Andy Clement
*/
public class SetValueTests extends ExpressionTestCase {
private final static boolean DEBUG = false;
@Test
public void testSetProperty() {
setValue("wonNobelPrize", true);
}
@Test
public void testSetNestedProperty() {
setValue("placeOfBirth.city", "Wien");
}
@Test
public void testSetArrayElementValue() {
setValue("inventions[0]", "Just the telephone");
}
@Test
public void testErrorCase() {
setValueExpectError("3=4", null);
}
@Test
public void testSetElementOfNull() {
setValueExpectError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
}
@Test
public void testSetArrayElementValueAllPrimitiveTypes() {
setValue("arrayContainer.ints[1]", 3);
setValue("arrayContainer.floats[1]", 3.0f);
setValue("arrayContainer.booleans[1]", false);
setValue("arrayContainer.doubles[1]", 3.4d);
setValue("arrayContainer.shorts[1]", (short)3);
setValue("arrayContainer.longs[1]", 3L);
setValue("arrayContainer.bytes[1]", (byte) 3);
setValue("arrayContainer.chars[1]", (char) 3);
}
@Test
public void testSetArrayElementValueAllPrimitiveTypesErrors() {
// none of these sets are possible due to (expected) conversion problems
setValueExpectError("arrayContainer.ints[1]", "wibble");
setValueExpectError("arrayContainer.floats[1]", "dribble");
setValueExpectError("arrayContainer.booleans[1]", "nein");
// TODO -- this fails with NPE due to ArrayToObject converter - discuss with Andy
//setValueExpectError("arrayContainer.doubles[1]", new ArrayList<String>());
//setValueExpectError("arrayContainer.shorts[1]", new ArrayList<String>());
//setValueExpectError("arrayContainer.longs[1]", new ArrayList<String>());
setValueExpectError("arrayContainer.bytes[1]", "NaB");
setValueExpectError("arrayContainer.chars[1]", "NaC");
}
@Test
public void testSetArrayElementNestedValue() {
setValue("placesLived[0].city", "Wien");
}
@Test
public void testSetListElementValue() {
setValue("placesLivedList[0]", new PlaceOfBirth("Wien"));
}
@Test
public void testSetGenericListElementValueTypeCoersion() {
// TODO currently failing since setValue does a getValue and "Wien" string != PlaceOfBirth - check with andy
setValue("placesLivedList[0]", "Wien");
}
@Test
public void testSetGenericListElementValueTypeCoersionOK() {
setValue("booleanList[0]", "true", Boolean.TRUE);
}
@Test
public void testSetListElementNestedValue() {
setValue("placesLived[0].city", "Wien");
}
@Test
public void testSetArrayElementInvalidIndex() {
setValueExpectError("placesLived[23]", "Wien");
setValueExpectError("placesLivedList[23]", "Wien");
}
@Test
public void testSetMapElements() {
setValue("testMap['montag']","lundi");
}
@Test
public void testIndexingIntoUnsupportedType() {
setValueExpectError("'hello'[3]", 'p');
}
@Test
public void testSetPropertyTypeCoersion() {
setValue("publicBoolean", "true", Boolean.TRUE);
}
@Test
public void testSetPropertyTypeCoersionThroughSetter() {
setValue("SomeProperty", "true", Boolean.TRUE);
}
@Test
public void testAssign() throws Exception {
StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
Expression e = parse("publicName='Andy'");
Assert.assertFalse(e.isWritable(eContext));
Assert.assertEquals("Andy",e.getValue(eContext));
}
/*
* Testing the coercion of both the keys and the values to the correct type
*/
@Test
public void testSetGenericMapElementRequiresCoercion() throws Exception {
StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
Expression e = parse("mapOfStringToBoolean[42]");
Assert.assertNull(e.getValue(eContext));
// Key should be coerced to string representation of 42
e.setValue(eContext, "true");
// All keys should be strings
Set ks = parse("mapOfStringToBoolean.keySet()").getValue(eContext,Set.class);
for (Object o: ks) {
Assert.assertEquals(String.class,o.getClass());
}
// All values should be booleans
Collection vs = parse("mapOfStringToBoolean.values()").getValue(eContext,Collection.class);
for (Object o: vs) {
Assert.assertEquals(Boolean.class,o.getClass());
}
// One final test check coercion on the key for a map lookup
Object o = e.getValue(eContext);
Assert.assertEquals(Boolean.TRUE,o);
}
private Expression parse(String expressionString) throws Exception {
return parser.parseExpression(expressionString);
}
/**
* Call setValue() but expect it to fail.
*/
protected void setValueExpectError(String expression, Object value) {
try {
Expression e = parser.parseExpression(expression);
if (e == null) {
Assert.fail("Parser returned null for expression");
}
if (DEBUG) {
SpelUtilities.printAbstractSyntaxTree(System.out, e);
}
StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext();
e.setValue(lContext, value);
Assert.fail("expected an error");
} catch (ParseException pe) {
pe.printStackTrace();
Assert.fail("Unexpected Exception: " + pe.getMessage());
} catch (EvaluationException ee) {
// success!
}
}
protected void setValue(String expression, Object value) {
try {
Expression e = parser.parseExpression(expression);
if (e == null) {
Assert.fail("Parser returned null for expression");
}
if (DEBUG) {
SpelUtilities.printAbstractSyntaxTree(System.out, e);
}
StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext();
Assert.assertTrue("Expression is not writeable but should be", e.isWritable(lContext));
e.setValue(lContext, value);
Assert.assertEquals("Retrieved value was not equal to set value", value, e.getValue(lContext,value.getClass()));
} catch (EvaluationException ee) {
ee.printStackTrace();
Assert.fail("Unexpected Exception: " + ee.getMessage());
} catch (ParseException pe) {
pe.printStackTrace();
Assert.fail("Unexpected Exception: " + pe.getMessage());
}
}
/**
* For use when coercion is happening during a setValue(). The expectedValue should be
* the coerced form of the value.
*/
protected void setValue(String expression, Object value, Object expectedValue) {
try {
Expression e = parser.parseExpression(expression);
if (e == null) {
Assert.fail("Parser returned null for expression");
}
if (DEBUG) {
SpelUtilities.printAbstractSyntaxTree(System.out, e);
}
StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext();
Assert.assertTrue("Expression is not writeable but should be", e.isWritable(lContext));
e.setValue(lContext, value);
Object a = expectedValue;
Object b = e.getValue(lContext);
if (!a.equals(b)) {
Assert.fail("Not the same: ["+a+"] type="+a.getClass()+" ["+b+"] type="+b.getClass());
// Assert.assertEquals("Retrieved value was not equal to set value", expectedValue, e.getValue(lContext));
}
} catch (EvaluationException ee) {
ee.printStackTrace();
Assert.fail("Unexpected Exception: " + ee.getMessage());
} catch (ParseException pe) {
pe.printStackTrace();
Assert.fail("Unexpected Exception: " + pe.getMessage());
}
}
}

View File

@@ -0,0 +1,515 @@
/*
* Copyright 2002-2009 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.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.testresources.Inventor;
import org.springframework.expression.spel.testresources.PlaceOfBirth;
/**
* Test the examples specified in the documentation.
*
* NOTE: any outgoing changes from this file upon synchronizing with the repo may indicate that
* you need to update the documentation too !
*
* @author Andy Clement
*/
public class SpelDocumentationTests extends ExpressionTestCase {
static Inventor tesla ;
static Inventor pupin ;
static {
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);
tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("SmilJan"));
tesla.setInventions(new String[] { "Telephone repeater", "Rotating magnetic field principle",
"Polyphase alternating-current system", "Induction motor", "Alternating-current power transmission",
"Tesla coil transformer", "Wireless communication", "Radio", "Fluorescent lights" });
pupin = new Inventor("Pupin", c.getTime(), "Idvor");
pupin.setPlaceOfBirth(new PlaceOfBirth("Idvor"));
}
static class IEEE {
private String name;
public Inventor[] Members = new Inventor[1];
public List Members2 = new ArrayList();
public Map<String,Object> officers = new HashMap<String,Object>();
public List<Map<String, Object>> reverse = new ArrayList<Map<String, Object>>();
@SuppressWarnings("unchecked")
IEEE() {
officers.put("president",pupin);
List linv = new ArrayList();
linv.add(tesla);
officers.put("advisors",linv);
Members2.add(tesla);
Members2.add(pupin);
reverse.add(officers);
}
public boolean isMember(String name) {
return true;
}
public String getName() { return name; }
public void setName(String n) { this.name = n; }
}
@Test
public void testMethodInvocation() {
evaluate("'Hello World'.concat('!')","Hello World!",String.class);
}
@Test
public void testBeanPropertyAccess() {
evaluate("new String('Hello World'.bytes)","Hello World",String.class);
}
@Test
public void testArrayLengthAccess() {
evaluate("'Hello World'.bytes.length",11,Integer.class);
}
@Test
public void testRootObject() throws Exception {
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);
// The constructor arguments are name, birthday, and nationaltiy.
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name");
StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(tesla);
String name = (String) exp.getValue(context);
Assert.assertEquals("Nikola Tesla",name);
}
@Test
public void testEqualityCheck() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(tesla);
Expression exp = parser.parseExpression("name == 'Nikola Tesla'");
boolean isEqual = exp.getValue(context, Boolean.class); // evaluates to true
Assert.assertTrue(isEqual);
}
// Section 7.4.1
@Test
public void testXMLBasedConfig() {
evaluate("(T(java.lang.Math).random() * 100.0 )>0",true,Boolean.class);
}
// Section 7.5
@Test
public void testLiterals() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); // evals to "Hello World"
Assert.assertEquals("Hello World",helloWorld);
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
Assert.assertEquals(6.0221415E+23,avogadrosNumber);
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); // evals to 2147483647
Assert.assertEquals(Integer.MAX_VALUE,maxValue);
boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
Assert.assertTrue(trueValue);
Object nullValue = parser.parseExpression("null").getValue();
Assert.assertNull(nullValue);
}
@Test
public void testPropertyAccess() throws Exception {
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); // 1856
Assert.assertEquals(1856,year);
String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);
Assert.assertEquals("SmilJan",city);
}
@Test
public void testPropertyNavigation() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
// Inventions Array
StandardEvaluationContext teslaContext = TestScenarioCreator.getTestEvaluationContext();
// teslaContext.setRootObject(tesla);
// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class);
Assert.assertEquals("Induction motor",invention);
// Members List
StandardEvaluationContext societyContext = new StandardEvaluationContext();
IEEE ieee = new IEEE();
ieee.Members[0]= tesla;
societyContext.setRootObject(ieee);
// evaluates to "Nikola Tesla"
String name = parser.parseExpression("Members[0].Name").getValue(societyContext, String.class);
Assert.assertEquals("Nikola Tesla",name);
// List and Array navigation
// evaluates to "Wireless communication"
invention = parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext, String.class);
Assert.assertEquals("Wireless communication",invention);
}
@Test
public void testDictionaryAccess() throws Exception {
StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE());
// Officer's Dictionary
Inventor pupin = parser.parseExpression("officers['president']").getValue(societyContext, Inventor.class);
// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].PlaceOfBirth.city").getValue(societyContext, String.class);
// setting values
Inventor i = parser.parseExpression("officers['advisors'][0]").getValue(societyContext,Inventor.class);
Assert.assertEquals("Nikola Tesla",i.getName());
parser.parseExpression("officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext, "Croatia");
Inventor i2 = parser.parseExpression("reverse[0]['advisors'][0]").getValue(societyContext,Inventor.class);
Assert.assertEquals("Nikola Tesla",i2.getName());
}
// 7.5.3
@Test
public void testMethodInvocation2() throws Exception {
// string literal, evaluates to "bc"
String c = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);
Assert.assertEquals("bc",c);
StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE());
// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class);
Assert.assertTrue(isMember);
}
// 7.5.4.1
@Test
public void testRelationalOperators() throws Exception {
boolean result = parser.parseExpression("2 == 2").getValue(Boolean.class);
Assert.assertTrue(result);
// evaluates to false
result = parser.parseExpression("2 < -5.0").getValue(Boolean.class);
Assert.assertFalse(result);
// evaluates to true
result = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
Assert.assertTrue(result);
}
@Test
public void testOtherOperators() throws Exception {
// evaluates to false
boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class);
Assert.assertFalse(falseValue);
// evaluates to true
boolean trueValue = parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
Assert.assertTrue(trueValue);
//evaluates to false
falseValue = parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
Assert.assertFalse(falseValue);
}
// 7.5.4.2
@Test
public void testLogicalOperators() throws Exception {
StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE());
// -- AND --
// evaluates to false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);
Assert.assertFalse(falseValue);
// evaluates to true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
// -- OR --
// evaluates to true
trueValue = parser.parseExpression("true or false").getValue(Boolean.class);
Assert.assertTrue(trueValue);
// evaluates to true
expression = "isMember('Nikola Tesla') or isMember('Albert Einstien')";
trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
Assert.assertTrue(trueValue);
// -- NOT --
// evaluates to false
falseValue = parser.parseExpression("!true").getValue(Boolean.class);
Assert.assertFalse(falseValue);
// -- AND and NOT --
expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
Assert.assertFalse(falseValue);
}
// 7.5.4.3
@Test
public void testNumericalOperators() throws Exception {
// Addition
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
Assert.assertEquals(2,two);
String testString = parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string'
Assert.assertEquals("test string",testString);
// Subtraction
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4
Assert.assertEquals(4,four);
double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000
Assert.assertEquals(-9000.0d,d);
// Multiplication
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6
Assert.assertEquals(6,six);
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0
Assert.assertEquals(24.0d,twentyFour);
// Division
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2
Assert.assertEquals(-2,minusTwo);
double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0
Assert.assertEquals(1.0d,one);
// Modulus
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3
Assert.assertEquals(3,three);
int oneInt = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1
Assert.assertEquals(1,oneInt);
// Operator precedence
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
Assert.assertEquals(-21,minusTwentyOne);
}
// 7.5.5
@Test
public void testAssignment() throws Exception {
Inventor inventor = new Inventor();
StandardEvaluationContext inventorContext = new StandardEvaluationContext();
inventorContext.setRootObject(inventor);
parser.parseExpression("foo").setValue(inventorContext, "Alexander Seovic2");
Assert.assertEquals("Alexander Seovic2",parser.parseExpression("foo").getValue(inventorContext,String.class));
// alternatively
String aleks = parser.parseExpression("foo = 'Alexandar Seovic'").getValue(inventorContext, String.class);
Assert.assertEquals("Alexandar Seovic",parser.parseExpression("foo").getValue(inventorContext,String.class));
Assert.assertEquals("Alexandar Seovic",aleks);
}
// 7.5.6
@Test
public void testTypes() throws Exception {
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
Assert.assertEquals(Date.class,dateClass);
boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class);
Assert.assertTrue(trueValue);
}
// 7.5.7
@Test
public void testConstructors() throws Exception {
StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE());
Inventor einstein =
parser.parseExpression("new org.springframework.expression.spel.testresources.Inventor('Albert Einstein',new java.util.Date(), 'German')").getValue(Inventor.class);
Assert.assertEquals("Albert Einstein", einstein.getName());
//create new inventor instance within add method of List
parser.parseExpression("Members2.add(new org.springframework.expression.spel.testresources.Inventor('Albert Einstein', 'German'))").getValue(societyContext);
}
// 7.5.8
@Test
public void testVariables() throws Exception {
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("newName", "Mike Tesla");
context.setRootObject(tesla);
parser.parseExpression("foo = #newName").getValue(context);
Assert.assertEquals("Mike Tesla",tesla.getFoo());
}
@SuppressWarnings("unchecked")
@Test
public void testSpecialVariables() throws Exception {
// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
// create parser and set variable 'primes' as the array of integers
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("primes",primes);
// all prime numbers > 10 from the list (using selection ?{...})
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(context);
Assert.assertEquals("[11, 13, 17]",primesGreaterThanTen.toString());
}
// 7.5.9
@Test
public void testFunctions() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.registerFunction("reverseString",
StringUtils.class.getDeclaredMethod("reverseString", new Class[] { String.class }));
String helloWorldReversed = parser.parseExpression("#reverseString('hello world')").getValue(context, String.class);
Assert.assertEquals("dlrow olleh",helloWorldReversed);
}
// 7.5.10
@Test
public void testTernary() throws Exception {
String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);
Assert.assertEquals("falseExp",falseString);
StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE());
parser.parseExpression("Name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla");
String expression = "isMember(#queryName)? #queryName + ' is a member of the ' " +
"+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";
String queryResultString = parser.parseExpression(expression).getValue(societyContext, String.class);
Assert.assertEquals("Nikola Tesla is a member of the IEEE Society",queryResultString);
// queryResultString = "Nikola Tesla is a member of the IEEE Society"
}
// 7.5.11
@SuppressWarnings("unchecked")
@Test
public void testSelection() throws Exception {
StandardEvaluationContext societyContext = new StandardEvaluationContext();
societyContext.setRootObject(new IEEE());
List<Inventor> list = (List<Inventor>) parser.parseExpression("Members2.?[nationality == 'Serbian']").getValue(societyContext);
Assert.assertEquals(1,list.size());
Assert.assertEquals("Nikola Tesla",list.get(0).getName());
}
// 7.5.12
@Test
public void testTemplating() throws Exception {
String randomPhrase =
parser.parseExpression("random number is ${T(java.lang.Math).random()}", new TemplatedParserContext()).getValue(String.class);
Assert.assertTrue(randomPhrase.startsWith("random number"));
}
static class TemplatedParserContext implements ParserContext {
public String getExpressionPrefix() {
return "${";
}
public String getExpressionSuffix() {
return "}";
}
public boolean isTemplate() {
return true;
}
}
static class StringUtils {
public static String reverseString(String input) {
StringBuilder backwards = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
backwards.append(input.charAt(input.length() - 1 - i));
}
return backwards.toString();
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2002-2009 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.PrintStream;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpression;
/**
* Utilities for working with Spring Expressions.
*
* @author Andy Clement
*/
public class SpelUtilities {
/**
* Output an indented representation of the expression syntax tree to the specified output stream.
* @param printStream the output stream to print into
* @param expression the expression to be displayed
*/
public static void printAbstractSyntaxTree(PrintStream printStream, Expression expression) {
printStream.println("===> Expression '" + expression.getExpressionString() + "' - AST start");
printAST(printStream, ((SpelExpression) expression).getAST(), "");
printStream.println("===> Expression '" + expression.getExpressionString() + "' - AST end");
}
/*
* Helper method for printing the AST with indentation
*/
private static void printAST(PrintStream out, SpelNode t, String indent) {
if (t != null) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append(t.getClass().getSimpleName());
sb.append(" value:").append(t.toStringAST());
sb.append(t.getChildCount() < 2 ? "" : " #children:" + t.getChildCount());
out.println(sb.toString());
for (int i = 0; i < t.getChildCount(); i++) {
printAST(out, t.getChild(i), indent + " ");
}
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2004-2008 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.util.List;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.support.StandardTypeLocator;
/**
* Unit tests for type comparison
*
* @author Andy Clement
*/
public class StandardTypeLocatorTests {
@Test
public void testImports() throws EvaluationException {
StandardTypeLocator locator = new StandardTypeLocator();
Assert.assertEquals(Integer.class,locator.findType("java.lang.Integer"));
Assert.assertEquals(String.class,locator.findType("java.lang.String"));
List<String> prefixes = locator.getImportPrefixes();
Assert.assertEquals(1,prefixes.size());
Assert.assertTrue(prefixes.contains("java.lang"));
Assert.assertFalse(prefixes.contains("java.util"));
Assert.assertEquals(Boolean.class,locator.findType("Boolean"));
// currently does not know about java.util by default
// assertEquals(java.util.List.class,locator.findType("List"));
try {
locator.findType("URL");
Assert.fail("Should have failed");
} catch (EvaluationException ee) {
SpelEvaluationException sEx = (SpelEvaluationException)ee;
Assert.assertEquals(SpelMessage.TYPE_NOT_FOUND,sEx.getMessageCode());
}
locator.registerImport("java.net");
Assert.assertEquals(java.net.URL.class,locator.findType("URL"));
}
}

View File

@@ -0,0 +1,275 @@
/*
* Copyright 2002-2009 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 junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.CompositeStringExpression;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* @author Andy Clement
* @author Juergen Hoeller
*/
public class TemplateExpressionParsingTests extends ExpressionTestCase {
public static final ParserContext DEFAULT_TEMPLATE_PARSER_CONTEXT = new ParserContext() {
public String getExpressionPrefix() {
return "${";
}
public String getExpressionSuffix() {
return "}";
}
public boolean isTemplate() {
return true;
}
};
public static final ParserContext HASH_DELIMITED_PARSER_CONTEXT = new ParserContext() {
public String getExpressionPrefix() {
return "#{";
}
public String getExpressionSuffix() {
return "}";
}
public boolean isTemplate() {
return true;
}
};
@Test
public void testParsingSimpleTemplateExpression01() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expr = parser.parseExpression("hello ${'world'}", DEFAULT_TEMPLATE_PARSER_CONTEXT);
Object o = expr.getValue();
Assert.assertEquals("hello world", o.toString());
}
@Test
public void testParsingSimpleTemplateExpression02() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expr = parser.parseExpression("hello ${'to'} you", DEFAULT_TEMPLATE_PARSER_CONTEXT);
Object o = expr.getValue();
Assert.assertEquals("hello to you", o.toString());
}
@Test
public void testParsingSimpleTemplateExpression03() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expr = parser.parseExpression("The quick ${'brown'} fox jumped over the ${'lazy'} dog",
DEFAULT_TEMPLATE_PARSER_CONTEXT);
Object o = expr.getValue();
Assert.assertEquals("The quick brown fox jumped over the lazy dog", o.toString());
}
@Test
public void testParsingSimpleTemplateExpression04() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expr = parser.parseExpression("${'hello'} world", DEFAULT_TEMPLATE_PARSER_CONTEXT);
Object o = expr.getValue();
Assert.assertEquals("hello world", o.toString());
expr = parser.parseExpression("", DEFAULT_TEMPLATE_PARSER_CONTEXT);
o = expr.getValue();
Assert.assertEquals("", o.toString());
expr = parser.parseExpression("abc", DEFAULT_TEMPLATE_PARSER_CONTEXT);
o = expr.getValue();
Assert.assertEquals("abc", o.toString());
expr = parser.parseExpression("abc", DEFAULT_TEMPLATE_PARSER_CONTEXT);
o = expr.getValue((Object)null);
Assert.assertEquals("abc", o.toString());
}
@Test
public void testCompositeStringExpression() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
Expression ex = parser.parseExpression("hello ${'world'}", DEFAULT_TEMPLATE_PARSER_CONTEXT);
checkString("hello world", ex.getValue());
checkString("hello world", ex.getValue(String.class));
checkString("hello world", ex.getValue((Object)null, String.class));
checkString("hello world", ex.getValue(new Rooty()));
checkString("hello world", ex.getValue(new Rooty(), String.class));
EvaluationContext ctx = new StandardEvaluationContext();
checkString("hello world", ex.getValue(ctx));
checkString("hello world", ex.getValue(ctx, String.class));
checkString("hello world", ex.getValue(ctx, null, String.class));
checkString("hello world", ex.getValue(ctx, new Rooty()));
checkString("hello world", ex.getValue(ctx, new Rooty(), String.class));
checkString("hello world", ex.getValue(ctx, new Rooty(), String.class));
Assert.assertEquals("hello ${'world'}", ex.getExpressionString());
Assert.assertFalse(ex.isWritable(new StandardEvaluationContext()));
Assert.assertFalse(ex.isWritable(new Rooty()));
Assert.assertFalse(ex.isWritable(new StandardEvaluationContext(), new Rooty()));
Assert.assertEquals(String.class,ex.getValueType());
Assert.assertEquals(String.class,ex.getValueType(ctx));
Assert.assertEquals(String.class,ex.getValueTypeDescriptor().getType());
Assert.assertEquals(String.class,ex.getValueTypeDescriptor(ctx).getType());
Assert.assertEquals(String.class,ex.getValueType(new Rooty()));
Assert.assertEquals(String.class,ex.getValueType(ctx, new Rooty()));
Assert.assertEquals(String.class,ex.getValueTypeDescriptor(new Rooty()).getType());
Assert.assertEquals(String.class,ex.getValueTypeDescriptor(ctx, new Rooty()).getType());
try {
ex.setValue(ctx, null);
Assert.fail();
} catch (EvaluationException ee) {
// success
}
try {
ex.setValue((Object)null, null);
Assert.fail();
} catch (EvaluationException ee) {
// success
}
try {
ex.setValue(ctx, null, null);
Assert.fail();
} catch (EvaluationException ee) {
// success
}
}
static class Rooty {}
@Test
public void testNestedExpressions() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
// treat the nested ${..} as a part of the expression
Expression ex = parser.parseExpression("hello ${listOfNumbersUpToTen.$[#this<5]} world",DEFAULT_TEMPLATE_PARSER_CONTEXT);
String s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
Assert.assertEquals("hello 4 world",s);
// not a useful expression but tests nested expression syntax that clashes with template prefix/suffix
ex = parser.parseExpression("hello ${listOfNumbersUpToTen.$[#root.listOfNumbersUpToTen.$[#this%2==1]==3]} world",DEFAULT_TEMPLATE_PARSER_CONTEXT);
Assert.assertEquals(CompositeStringExpression.class,ex.getClass());
CompositeStringExpression cse = (CompositeStringExpression)ex;
Expression[] exprs = cse.getExpressions();
Assert.assertEquals(3,exprs.length);
Assert.assertEquals("listOfNumbersUpToTen.$[#root.listOfNumbersUpToTen.$[#this%2==1]==3]",exprs[1].getExpressionString());
s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
Assert.assertEquals("hello world",s);
ex = parser.parseExpression("hello ${listOfNumbersUpToTen.$[#this<5]} ${listOfNumbersUpToTen.$[#this>5]} world",DEFAULT_TEMPLATE_PARSER_CONTEXT);
s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
Assert.assertEquals("hello 4 10 world",s);
try {
ex = parser.parseExpression("hello ${listOfNumbersUpToTen.$[#this<5]} ${listOfNumbersUpToTen.$[#this>5] world",DEFAULT_TEMPLATE_PARSER_CONTEXT);
Assert.fail("Should have failed");
} catch (ParseException pe) {
Assert.assertEquals("No ending suffix '}' for expression starting at character 41: ${listOfNumbersUpToTen.$[#this>5] world",pe.getMessage());
}
try {
ex = parser.parseExpression("hello ${listOfNumbersUpToTen.$[#root.listOfNumbersUpToTen.$[#this%2==1==3]} world",DEFAULT_TEMPLATE_PARSER_CONTEXT);
Assert.fail("Should have failed");
} catch (ParseException pe) {
Assert.assertEquals("Found closing '}' at position 74 but most recent opening is '[' at position 30",pe.getMessage());
}
}
@Test
public void testClashingWithSuffixes() throws Exception {
// Just wanting to use the prefix or suffix within the template:
Expression ex = parser.parseExpression("hello ${3+4} world",DEFAULT_TEMPLATE_PARSER_CONTEXT);
String s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
Assert.assertEquals("hello 7 world",s);
ex = parser.parseExpression("hello ${3+4} wo${'${'}rld",DEFAULT_TEMPLATE_PARSER_CONTEXT);
s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
Assert.assertEquals("hello 7 wo${rld",s);
ex = parser.parseExpression("hello ${3+4} wo}rld",DEFAULT_TEMPLATE_PARSER_CONTEXT);
s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
Assert.assertEquals("hello 7 wo}rld",s);
}
@Test
public void testParsingNormalExpressionThroughTemplateParser() throws Exception {
Expression expr = parser.parseExpression("1+2+3");
Assert.assertEquals(6,expr.getValue());
expr = parser.parseExpression("1+2+3",null);
Assert.assertEquals(6,expr.getValue());
}
@Test
public void testErrorCases() throws Exception {
try {
parser.parseExpression("hello ${'world'", DEFAULT_TEMPLATE_PARSER_CONTEXT);
Assert.fail("Should have failed");
} catch (ParseException pe) {
Assert.assertEquals("No ending suffix '}' for expression starting at character 6: ${'world'",pe.getMessage());
Assert.assertEquals("hello ${'world'",pe.getExpressionString());
}
try {
parser.parseExpression("hello ${'wibble'${'world'}", DEFAULT_TEMPLATE_PARSER_CONTEXT);
Assert.fail("Should have failed");
} catch (ParseException pe) {
Assert.assertEquals("No ending suffix '}' for expression starting at character 6: ${'wibble'${'world'}",pe.getMessage());
}
try {
parser.parseExpression("hello ${} world", DEFAULT_TEMPLATE_PARSER_CONTEXT);
Assert.fail("Should have failed");
} catch (ParseException pe) {
Assert.assertEquals("No expression defined within delimiter '${}' at character 6",pe.getMessage());
}
}
@Test
public void testTemplateParserContext() {
TemplateParserContext tpc = new TemplateParserContext("abc","def");
Assert.assertEquals("abc", tpc.getExpressionPrefix());
Assert.assertEquals("def", tpc.getExpressionSuffix());
Assert.assertTrue(tpc.isTemplate());
tpc = new TemplateParserContext();
Assert.assertEquals("#{", tpc.getExpressionPrefix());
Assert.assertEquals("}", tpc.getExpressionSuffix());
Assert.assertTrue(tpc.isTemplate());
ParserContext pc = ParserContext.TEMPLATE_EXPRESSION;
Assert.assertEquals("#{", pc.getExpressionPrefix());
Assert.assertEquals("}", pc.getExpressionSuffix());
Assert.assertTrue(pc.isTemplate());
}
// ---
private void checkString(String expectedString, Object value) {
if (!(value instanceof String)) {
Assert.fail("Result was not a string, it was of type " + value.getClass() + " (value=" + value + ")");
}
if (!value.equals(expectedString)) {
Assert.fail("Did not get expected result. Should have been '" + expectedString + "' but was '" + value + "'");
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2002-2009 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.util.GregorianCalendar;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.testresources.Inventor;
import org.springframework.expression.spel.testresources.PlaceOfBirth;
///CLOVER:OFF
/**
* Builds an evaluation context for test expressions. Features of the test evaluation context are:
* <ul>
* <li>The root context object is an Inventor instance {@link Inventor}
* </ul>
*/
public class TestScenarioCreator {
public static StandardEvaluationContext getTestEvaluationContext() {
StandardEvaluationContext testContext = new StandardEvaluationContext();
setupRootContextObject(testContext);
populateVariables(testContext);
populateFunctions(testContext);
return testContext;
}
/**
* Register some Java reflect methods as well known functions that can be called from an expression.
* @param testContext the test evaluation context
*/
private static void populateFunctions(StandardEvaluationContext testContext) {
try {
testContext.registerFunction("isEven", TestScenarioCreator.class.getDeclaredMethod("isEven",
new Class[] { Integer.TYPE }));
testContext.registerFunction("reverseInt", TestScenarioCreator.class.getDeclaredMethod("reverseInt",
new Class[] { Integer.TYPE, Integer.TYPE, Integer.TYPE }));
testContext.registerFunction("reverseString", TestScenarioCreator.class.getDeclaredMethod("reverseString",
new Class[] { String.class }));
testContext.registerFunction("varargsFunctionReverseStringsAndMerge", TestScenarioCreator.class
.getDeclaredMethod("varargsFunctionReverseStringsAndMerge", new Class[] { String[].class }));
testContext.registerFunction("varargsFunctionReverseStringsAndMerge2", TestScenarioCreator.class
.getDeclaredMethod("varargsFunctionReverseStringsAndMerge2", new Class[] { Integer.TYPE,
String[].class }));
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
/**
* Register some variables that can be referenced from the tests
* @param testContext the test evaluation context
*/
private static void populateVariables(StandardEvaluationContext testContext) {
testContext.setVariable("answer", 42);
}
/**
* Create the root context object, an Inventor instance. Non-qualified property and method references will be
* resolved against this context object.
*
* @param testContext the evaluation context in which to set the root object
*/
private static void setupRootContextObject(StandardEvaluationContext testContext) {
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("SmilJan"));
tesla.setInventions(new String[] { "Telephone repeater", "Rotating magnetic field principle",
"Polyphase alternating-current system", "Induction motor", "Alternating-current power transmission",
"Tesla coil transformer", "Wireless communication", "Radio", "Fluorescent lights" });
testContext.setRootObject(tesla);
}
// These methods are registered in the test context and therefore accessible through function calls
// in test expressions
public static String isEven(int i) {
if ((i % 2) == 0)
return "y";
return "n";
}
public static int[] reverseInt(int i, int j, int k) {
return new int[] { k, j, i };
}
public static String reverseString(String input) {
StringBuilder backwards = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
backwards.append(input.charAt(input.length() - 1 - i));
}
return backwards.toString();
}
public static String varargsFunctionReverseStringsAndMerge(String... strings) {
StringBuilder sb = new StringBuilder();
if (strings != null) {
for (int i = strings.length - 1; i >= 0; i--) {
sb.append(strings[i]);
}
}
return sb.toString();
}
public static String varargsFunctionReverseStringsAndMerge2(int j, String... strings) {
StringBuilder sb = new StringBuilder();
sb.append(j);
if (strings != null) {
for (int i = strings.length - 1; i >= 0; i--) {
sb.append(strings[i]);
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2002-2009 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 junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* Tests the evaluation of expressions that access variables and functions (lambda/java).
*
* @author Andy Clement
*/
public class VariableAndFunctionTests extends ExpressionTestCase {
@Test
public void testVariableAccess01() {
evaluate("#answer", "42", Integer.class, SHOULD_BE_WRITABLE);
evaluate("#answer / 2", 21, Integer.class, SHOULD_NOT_BE_WRITABLE);
}
@Test
public void testVariableAccess_WellKnownVariables() {
evaluate("#this.getName()","Nikola Tesla",String.class);
evaluate("#root.getName()","Nikola Tesla",String.class);
}
@Test
public void testFunctionAccess01() {
evaluate("#reverseInt(1,2,3)", "int[3]{3,2,1}", int[].class);
evaluate("#reverseInt('1',2,3)", "int[3]{3,2,1}", int[].class); // requires type conversion of '1' to 1
evaluateAndCheckError("#reverseInt(1)", SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, 0, 1, 3);
}
@Test
public void testFunctionAccess02() {
evaluate("#reverseString('hello')", "olleh", String.class);
evaluate("#reverseString(37)", "73", String.class); // requires type conversion of 37 to '37'
}
@Test
public void testCallVarargsFunction() {
evaluate("#varargsFunctionReverseStringsAndMerge('a','b','c')", "cba", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge('a')", "a", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge()", "", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge('b',25)", "25b", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge(25)", "25", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(1,'a','b','c')", "1cba", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(2,'a')", "2a", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(3)", "3", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(4,'b',25)", "425b", String.class);
evaluate("#varargsFunctionReverseStringsAndMerge2(5,25)", "525", String.class);
}
@Test
public void testCallingIllegalFunctions() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.setVariable("notStatic", this.getClass().getMethod("nonStatic"));
try {
@SuppressWarnings("unused")
Object v = parser.parseRaw("#notStatic()").getValue(ctx);
Assert.fail("Should have failed with exception - cannot call non static method that way");
} catch (SpelEvaluationException se) {
if (se.getMessageCode() != SpelMessage.FUNCTION_MUST_BE_STATIC) {
se.printStackTrace();
Assert.fail("Should have failed a message about the function needing to be static, not: "
+ se.getMessageCode());
}
}
}
// this method is used by the test above
public void nonStatic() {
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-2011 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.ast;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import static org.junit.Assert.*;
/**
* @author Andy Wilkinson
*/
public class FormatHelperTests {
@Test
public void formatMethodWithSingleArgumentForMessage() {
String message = FormatHelper.formatMethodForMessage("foo", Arrays.asList(TypeDescriptor.forObject("a string")));
assertEquals("foo(java.lang.String)", message);
}
@Test
public void formatMethodWithMultipleArgumentsForMessage() {
String message = FormatHelper.formatMethodForMessage("foo", Arrays.asList(TypeDescriptor.forObject("a string"), TypeDescriptor.forObject(Integer.valueOf(5))));
assertEquals("foo(java.lang.String,java.lang.Integer)", message);
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright 2002-2010 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.standard;
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import org.junit.Test;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* @author Mark Fisher
*/
public class PropertiesConversionSpelTests {
private static final SpelExpressionParser parser = new SpelExpressionParser();
@Test
public void props() {
Properties props = new Properties();
props.setProperty("x", "1");
props.setProperty("y", "2");
props.setProperty("z", "3");
Expression expression = parser.parseExpression("foo(#props)");
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("props", props);
String result = expression.getValue(context, new TestBean(), String.class);
assertEquals("123", result);
}
@Test
public void mapWithAllStringValues() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("x", "1");
map.put("y", "2");
map.put("z", "3");
Expression expression = parser.parseExpression("foo(#props)");
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("props", map);
String result = expression.getValue(context, new TestBean(), String.class);
assertEquals("123", result);
}
@Test
public void mapWithNonStringValue() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("x", "1");
map.put("y", 2);
map.put("z", "3");
map.put("a", new UUID(1, 1));
Expression expression = parser.parseExpression("foo(#props)");
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("props", map);
String result = expression.getValue(context, new TestBean(), String.class);
assertEquals("1null3", result);
}
@Test
public void customMapWithNonStringValue() {
CustomMap map = new CustomMap();
map.put("x", "1");
map.put("y", 2);
map.put("z", "3");
Expression expression = parser.parseExpression("foo(#props)");
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("props", map);
String result = expression.getValue(context, new TestBean(), String.class);
assertEquals("1null3", result);
}
private static class TestBean {
@SuppressWarnings("unused")
public String foo(Properties props) {
return props.getProperty("x") + props.getProperty("y") + props.getProperty("z");
}
}
@SuppressWarnings("serial")
private static class CustomMap extends HashMap<String, Object> {
}
}

View File

@@ -0,0 +1,370 @@
/*
* Copyright 2002-2009 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.standard;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.ExpressionException;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.SpelParseException;
import org.springframework.expression.spel.ast.OpAnd;
import org.springframework.expression.spel.ast.OpOr;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* @author Andy Clement
*/
public class SpelParserTests {
@Test
public void theMostBasic() throws EvaluationException,ParseException {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2");
Assert.assertNotNull(expr);
Assert.assertNotNull(expr.getAST());
Assert.assertEquals(2,expr.getValue());
Assert.assertEquals(Integer.class,expr.getValueType());
Assert.assertEquals(2,expr.getAST().getValue(null));
}
@Test
public void valueType() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
EvaluationContext ctx = new StandardEvaluationContext();
Class c = parser.parseRaw("2").getValueType();
Assert.assertEquals(Integer.class,c);
c = parser.parseRaw("12").getValueType(ctx);
Assert.assertEquals(Integer.class,c);
c = parser.parseRaw("null").getValueType();
Assert.assertNull(c);
c = parser.parseRaw("null").getValueType(ctx);
Assert.assertNull(c);
Object o = parser.parseRaw("null").getValue(ctx,Integer.class);
Assert.assertNull(o);
}
@Test
public void whitespace() throws EvaluationException,ParseException {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2 + 3");
Assert.assertEquals(5,expr.getValue());
expr = parser.parseRaw("2 + 3");
Assert.assertEquals(5,expr.getValue());
expr = parser.parseRaw("2\n+ 3");
Assert.assertEquals(5,expr.getValue());
expr = parser.parseRaw("2\r\n+\t3");
Assert.assertEquals(5,expr.getValue());
}
@Test
public void arithmeticPlus1() throws EvaluationException,ParseException {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2+2");
Assert.assertNotNull(expr);
Assert.assertNotNull(expr.getAST());
Assert.assertEquals(4,expr.getValue());
}
@Test
public void arithmeticPlus2() throws EvaluationException,ParseException {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("37+41");
Assert.assertEquals(78,expr.getValue());
}
@Test
public void arithmeticMultiply1() throws EvaluationException,ParseException {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2*3");
Assert.assertNotNull(expr);
Assert.assertNotNull(expr.getAST());
// printAst(expr.getAST(),0);
Assert.assertEquals(6,expr.getValue());
}
@Test
public void arithmeticPrecedence1() throws EvaluationException,ParseException {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2*3+5");
Assert.assertEquals(11,expr.getValue());
}
@Test
public void generalExpressions() throws Exception {
try {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("new String");
Assert.fail();
} catch (ParseException e) {
Assert.assertTrue(e instanceof SpelParseException);
SpelParseException spe = (SpelParseException)e;
Assert.assertEquals(SpelMessage.MISSING_CONSTRUCTOR_ARGS,spe.getMessageCode());
Assert.assertEquals(10,spe.getPosition());
}
try {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("new String(3,");
Assert.fail();
} catch (ParseException e) {
Assert.assertTrue(e instanceof SpelParseException);
SpelParseException spe = (SpelParseException)e;
Assert.assertEquals(SpelMessage.RUN_OUT_OF_ARGUMENTS,spe.getMessageCode());
Assert.assertEquals(10,spe.getPosition());
}
try {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("new String(3");
Assert.fail();
} catch (ParseException e) {
Assert.assertTrue(e instanceof SpelParseException);
SpelParseException spe = (SpelParseException)e;
Assert.assertEquals(SpelMessage.RUN_OUT_OF_ARGUMENTS,spe.getMessageCode());
Assert.assertEquals(10,spe.getPosition());
}
try {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("new String(");
Assert.fail();
} catch (ParseException e) {
Assert.assertTrue(e instanceof SpelParseException);
SpelParseException spe = (SpelParseException)e;
Assert.assertEquals(SpelMessage.RUN_OUT_OF_ARGUMENTS,spe.getMessageCode());
Assert.assertEquals(10,spe.getPosition());
}
try {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("\"abc");
Assert.fail();
} catch (ParseException e) {
Assert.assertTrue(e instanceof SpelParseException);
SpelParseException spe = (SpelParseException)e;
Assert.assertEquals(SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING,spe.getMessageCode());
Assert.assertEquals(0,spe.getPosition());
}
try {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("'abc");
Assert.fail();
} catch (ParseException e) {
Assert.assertTrue(e instanceof SpelParseException);
SpelParseException spe = (SpelParseException)e;
Assert.assertEquals(SpelMessage.NON_TERMINATING_QUOTED_STRING,spe.getMessageCode());
Assert.assertEquals(0,spe.getPosition());
}
}
@Test
public void arithmeticPrecedence2() throws EvaluationException,ParseException {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2+3*5");
Assert.assertEquals(17,expr.getValue());
}
@Test
public void arithmeticPrecedence3() throws EvaluationException,ParseException {
SpelExpression expr = new SpelExpressionParser().parseRaw("3+10/2");
Assert.assertEquals(8,expr.getValue());
}
@Test
public void arithmeticPrecedence4() throws EvaluationException,ParseException {
SpelExpression expr = new SpelExpressionParser().parseRaw("10/2+3");
Assert.assertEquals(8,expr.getValue());
}
@Test
public void arithmeticPrecedence5() throws EvaluationException,ParseException {
SpelExpression expr = new SpelExpressionParser().parseRaw("(4+10)/2");
Assert.assertEquals(7,expr.getValue());
}
@Test
public void arithmeticPrecedence6() throws EvaluationException,ParseException {
SpelExpression expr = new SpelExpressionParser().parseRaw("(3+2)*2");
Assert.assertEquals(10,expr.getValue());
}
@Test
public void booleanOperators() throws EvaluationException,ParseException {
SpelExpression expr = new SpelExpressionParser().parseRaw("true");
Assert.assertEquals(Boolean.TRUE,expr.getValue(Boolean.class));
expr = new SpelExpressionParser().parseRaw("false");
Assert.assertEquals(Boolean.FALSE,expr.getValue(Boolean.class));
expr = new SpelExpressionParser().parseRaw("false and false");
Assert.assertEquals(Boolean.FALSE,expr.getValue(Boolean.class));
expr = new SpelExpressionParser().parseRaw("true and (true or false)");
Assert.assertEquals(Boolean.TRUE,expr.getValue(Boolean.class));
expr = new SpelExpressionParser().parseRaw("true and true or false");
Assert.assertEquals(Boolean.TRUE,expr.getValue(Boolean.class));
expr = new SpelExpressionParser().parseRaw("!true");
Assert.assertEquals(Boolean.FALSE,expr.getValue(Boolean.class));
expr = new SpelExpressionParser().parseRaw("!(false or true)");
Assert.assertEquals(Boolean.FALSE,expr.getValue(Boolean.class));
}
@Test
public void testStringLiterals() throws EvaluationException,ParseException {
SpelExpression expr = new SpelExpressionParser().parseRaw("'howdy'");
Assert.assertEquals("howdy",expr.getValue());
expr = new SpelExpressionParser().parseRaw("'hello '' world'");
Assert.assertEquals("hello ' world",expr.getValue());
}
@Test
public void testStringLiterals2() throws EvaluationException,ParseException {
SpelExpression expr = new SpelExpressionParser().parseRaw("'howdy'.substring(0,2)");
Assert.assertEquals("ho",expr.getValue());
}
@Test
public void testPositionalInformation() throws EvaluationException, ParseException {
SpelExpression expr = new SpelExpressionParser().parseRaw("true and true or false");
SpelNode rootAst = expr.getAST();
OpOr operatorOr = (OpOr)rootAst;
OpAnd operatorAnd = (OpAnd)operatorOr.getLeftOperand();
SpelNode rightOrOperand = operatorOr.getRightOperand();
// check position for final 'false'
Assert.assertEquals(17, rightOrOperand.getStartPosition());
Assert.assertEquals(22, rightOrOperand.getEndPosition());
// check position for first 'true'
Assert.assertEquals(0, operatorAnd.getLeftOperand().getStartPosition());
Assert.assertEquals(4, operatorAnd.getLeftOperand().getEndPosition());
// check position for second 'true'
Assert.assertEquals(9, operatorAnd.getRightOperand().getStartPosition());
Assert.assertEquals(13, operatorAnd.getRightOperand().getEndPosition());
// check position for OperatorAnd
Assert.assertEquals(5, operatorAnd.getStartPosition());
Assert.assertEquals(8, operatorAnd.getEndPosition());
// check position for OperatorOr
Assert.assertEquals(14, operatorOr.getStartPosition());
Assert.assertEquals(16, operatorOr.getEndPosition());
}
@Test
public void testTokenKind() {
TokenKind tk = TokenKind.NOT;
Assert.assertFalse(tk.hasPayload());
Assert.assertEquals("NOT(!)",tk.toString());
tk = TokenKind.MINUS;
Assert.assertFalse(tk.hasPayload());
Assert.assertEquals("MINUS(-)",tk.toString());
tk = TokenKind.LITERAL_STRING;
Assert.assertEquals("LITERAL_STRING",tk.toString());
Assert.assertTrue(tk.hasPayload());
}
@Test
public void testToken() {
Token token = new Token(TokenKind.NOT,0,3);
Assert.assertEquals(TokenKind.NOT,token.kind);
Assert.assertEquals(0,token.startpos);
Assert.assertEquals(3,token.endpos);
Assert.assertEquals("[NOT(!)](0,3)",token.toString());
token = new Token(TokenKind.LITERAL_STRING,"abc".toCharArray(),0,3);
Assert.assertEquals(TokenKind.LITERAL_STRING,token.kind);
Assert.assertEquals(0,token.startpos);
Assert.assertEquals(3,token.endpos);
Assert.assertEquals("[LITERAL_STRING:abc](0,3)",token.toString());
}
@Test
public void testExceptions() {
ExpressionException exprEx = new ExpressionException("test");
Assert.assertEquals("test", exprEx.getMessage());
Assert.assertEquals("test", exprEx.toDetailedString());
exprEx = new ExpressionException("wibble","test");
Assert.assertEquals("test", exprEx.getMessage());
Assert.assertEquals("Expression 'wibble': test", exprEx.toDetailedString());
exprEx = new ExpressionException("wibble",3, "test");
Assert.assertEquals("test", exprEx.getMessage());
Assert.assertEquals("Expression 'wibble' @ 3: test", exprEx.toDetailedString());
}
@Test
public void testNumerics() {
checkNumber("2",2,Integer.class);
checkNumber("22",22,Integer.class);
checkNumber("+22",22,Integer.class);
checkNumber("-22",-22,Integer.class);
checkNumber("2L",2L,Long.class);
checkNumber("22l",22L,Long.class);
checkNumber("0x1",1,Integer.class);
checkNumber("0x1L",1L,Long.class);
checkNumber("0xa",10,Integer.class);
checkNumber("0xAL",10L,Long.class);
checkNumberError("0x",SpelMessage.NOT_AN_INTEGER);
checkNumberError("0xL",SpelMessage.NOT_A_LONG);
checkNumberError(".324",SpelMessage.UNEXPECTED_DATA_AFTER_DOT);
checkNumberError("3.4L",SpelMessage.REAL_CANNOT_BE_LONG);
// Number is parsed as a float, but immediately promoted to a double
checkNumber("3.5f",3.5d,Double.class);
checkNumber("1.2e3", 1.2e3d, Double.class);
checkNumber("1.2e+3", 1.2e3d, Double.class);
checkNumber("1.2e-3", 1.2e-3d, Double.class);
checkNumber("1.2e3", 1.2e3d, Double.class);
checkNumber("1.e+3", 1.e3d, Double.class);
checkNumber("1e+3", 1e3d, Double.class);
}
private void checkNumber(String expression, Object value, Class<?> type) {
try {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw(expression);
Object o = expr.getValue();
Assert.assertEquals(value,o);
Assert.assertEquals(type,o.getClass());
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
private void checkNumberError(String expression, SpelMessage expectedMessage) {
try {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw(expression);
Assert.fail();
} catch (ParseException e) {
Assert.assertTrue(e instanceof SpelParseException);
SpelParseException spe = (SpelParseException)e;
Assert.assertEquals(expectedMessage,spe.getMessageCode());
}
}
}

View File

@@ -0,0 +1,515 @@
/*
* Copyright 2002-2010 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.support;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ParseException;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionTestCase;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelUtilities;
import org.springframework.expression.spel.ast.FormatHelper;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.support.ReflectionHelper.ArgsMatchKind;
/**
* Tests for any helper code.
*
* @author Andy Clement
*/
public class ReflectionHelperTests extends ExpressionTestCase {
@Test
public void testFormatHelperForClassName() {
Assert.assertEquals("java.lang.String",FormatHelper.formatClassNameForMessage(String.class));
Assert.assertEquals("java.lang.String[]",FormatHelper.formatClassNameForMessage(new String[1].getClass()));
Assert.assertEquals("int[]",FormatHelper.formatClassNameForMessage(new int[1].getClass()));
Assert.assertEquals("int[][]",FormatHelper.formatClassNameForMessage(new int[1][2].getClass()));
Assert.assertEquals("null",FormatHelper.formatClassNameForMessage(null));
}
/*
@Test
public void testFormatHelperForMethod() {
Assert.assertEquals("foo(java.lang.String)",FormatHelper.formatMethodForMessage("foo", String.class));
Assert.assertEquals("goo(java.lang.String,int[])",FormatHelper.formatMethodForMessage("goo", String.class,new int[1].getClass()));
Assert.assertEquals("boo()",FormatHelper.formatMethodForMessage("boo"));
}
*/
@Test
public void testUtilities() throws ParseException {
SpelExpression expr = (SpelExpression)parser.parseExpression("3+4+5+6+7-2");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
SpelUtilities.printAbstractSyntaxTree(ps, expr);
ps.flush();
String s = baos.toString();
// ===> Expression '3+4+5+6+7-2' - AST start
// OperatorMinus value:(((((3 + 4) + 5) + 6) + 7) - 2) #children:2
// OperatorPlus value:((((3 + 4) + 5) + 6) + 7) #children:2
// OperatorPlus value:(((3 + 4) + 5) + 6) #children:2
// OperatorPlus value:((3 + 4) + 5) #children:2
// OperatorPlus value:(3 + 4) #children:2
// CompoundExpression value:3
// IntLiteral value:3
// CompoundExpression value:4
// IntLiteral value:4
// CompoundExpression value:5
// IntLiteral value:5
// CompoundExpression value:6
// IntLiteral value:6
// CompoundExpression value:7
// IntLiteral value:7
// CompoundExpression value:2
// IntLiteral value:2
// ===> Expression '3+4+5+6+7-2' - AST end
Assert.assertTrue(s.indexOf("===> Expression '3+4+5+6+7-2' - AST start")!=-1);
Assert.assertTrue(s.indexOf(" OpPlus value:((((3 + 4) + 5) + 6) + 7) #children:2")!=-1);
}
@Test
public void testTypedValue() {
TypedValue tValue = new TypedValue("hello");
Assert.assertEquals(String.class,tValue.getTypeDescriptor().getType());
Assert.assertEquals("TypedValue: 'hello' of [java.lang.String]",tValue.toString());
}
@Test
public void testReflectionHelperCompareArguments_ExactMatching() {
StandardTypeConverter typeConverter = new StandardTypeConverter();
// Calling foo(String) with (String) is exact match
checkMatch(new Class[]{String.class},new Class[]{String.class},typeConverter,ArgsMatchKind.EXACT);
// Calling foo(String,Integer) with (String,Integer) is exact match
checkMatch(new Class[]{String.class,Integer.class},new Class[]{String.class,Integer.class},typeConverter,ArgsMatchKind.EXACT);
}
@Test
public void testReflectionHelperCompareArguments_CloseMatching() {
StandardTypeConverter typeConverter = new StandardTypeConverter();
// Calling foo(List) with (ArrayList) is close match (no conversion required)
checkMatch(new Class[]{ArrayList.class},new Class[]{List.class},typeConverter,ArgsMatchKind.CLOSE);
// Passing (Sub,String) on call to foo(Super,String) is close match
checkMatch(new Class[]{Sub.class,String.class},new Class[]{Super.class,String.class},typeConverter,ArgsMatchKind.CLOSE);
// Passing (String,Sub) on call to foo(String,Super) is close match
checkMatch(new Class[]{String.class,Sub.class},new Class[]{String.class,Super.class},typeConverter,ArgsMatchKind.CLOSE);
}
@Test
public void testReflectionHelperCompareArguments_RequiresConversionMatching() {
StandardTypeConverter typeConverter = new StandardTypeConverter();
// Calling foo(String,int) with (String,Integer) requires boxing conversion of argument one
checkMatch(new Class[]{String.class,Integer.TYPE},new Class[]{String.class,Integer.class},typeConverter,ArgsMatchKind.CLOSE,1);
// Passing (int,String) on call to foo(Integer,String) requires boxing conversion of argument zero
checkMatch(new Class[]{Integer.TYPE,String.class},new Class[]{Integer.class, String.class},typeConverter,ArgsMatchKind.CLOSE,0);
// Passing (int,Sub) on call to foo(Integer,Super) requires boxing conversion of argument zero
checkMatch(new Class[]{Integer.TYPE,Sub.class},new Class[]{Integer.class, Super.class},typeConverter,ArgsMatchKind.CLOSE,0);
// Passing (int,Sub,boolean) on call to foo(Integer,Super,Boolean) requires boxing conversion of arguments zero and two
// TODO checkMatch(new Class[]{Integer.TYPE,Sub.class,Boolean.TYPE},new Class[]{Integer.class, Super.class,Boolean.class},typeConverter,ArgsMatchKind.REQUIRES_CONVERSION,0,2);
}
@Test
public void testReflectionHelperCompareArguments_NotAMatch() {
StandardTypeConverter typeConverter = new StandardTypeConverter();
// Passing (Super,String) on call to foo(Sub,String) is not a match
checkMatch(new Class[]{Super.class,String.class},new Class[]{Sub.class,String.class},typeConverter,null);
}
@Test
public void testReflectionHelperCompareArguments_Varargs_ExactMatching() {
StandardTypeConverter tc = new StandardTypeConverter();
Class<?> stringArrayClass = new String[0].getClass();
Class<?> integerArrayClass = new Integer[0].getClass();
// Passing (String[]) on call to (String[]) is exact match
checkMatch2(new Class[]{stringArrayClass},new Class[]{stringArrayClass},tc,ArgsMatchKind.EXACT);
// Passing (Integer, String[]) on call to (Integer, String[]) is exact match
checkMatch2(new Class[]{Integer.class,stringArrayClass},new Class[]{Integer.class,stringArrayClass},tc,ArgsMatchKind.EXACT);
// Passing (String, Integer, String[]) on call to (String, String, String[]) is exact match
checkMatch2(new Class[]{String.class,Integer.class,stringArrayClass},new Class[]{String.class,Integer.class,stringArrayClass},tc,ArgsMatchKind.EXACT);
// Passing (Sub, String[]) on call to (Super, String[]) is exact match
checkMatch2(new Class[]{Sub.class,stringArrayClass},new Class[]{Super.class,stringArrayClass},tc,ArgsMatchKind.CLOSE);
// Passing (Integer, String[]) on call to (String, String[]) is exact match
checkMatch2(new Class[]{Integer.class,stringArrayClass},new Class[]{String.class,stringArrayClass},tc,ArgsMatchKind.REQUIRES_CONVERSION,0);
// Passing (Integer, Sub, String[]) on call to (String, Super, String[]) is exact match
checkMatch2(new Class[]{Integer.class,Sub.class,String[].class},new Class[]{String.class,Super.class,String[].class},tc,ArgsMatchKind.REQUIRES_CONVERSION,0);
// Passing (String) on call to (String[]) is exact match
checkMatch2(new Class[]{String.class},new Class[]{stringArrayClass},tc,ArgsMatchKind.EXACT);
// Passing (Integer,String) on call to (Integer,String[]) is exact match
checkMatch2(new Class[]{Integer.class,String.class},new Class[]{Integer.class,stringArrayClass},tc,ArgsMatchKind.EXACT);
// Passing (String) on call to (Integer[]) is conversion match (String to Integer)
checkMatch2(new Class[]{String.class},new Class[]{integerArrayClass},tc,ArgsMatchKind.REQUIRES_CONVERSION,0);
// Passing (Sub) on call to (Super[]) is close match
checkMatch2(new Class[]{Sub.class},new Class[]{new Super[0].getClass()},tc,ArgsMatchKind.CLOSE);
// Passing (Super) on call to (Sub[]) is not a match
checkMatch2(new Class[]{Super.class},new Class[]{new Sub[0].getClass()},tc,null);
checkMatch2(new Class[]{Unconvertable.class,String.class},new Class[]{Sub.class,Super[].class},tc,null);
checkMatch2(new Class[]{Integer.class,Integer.class,String.class},new Class[]{String.class,String.class,Super[].class},tc,null);
checkMatch2(new Class[]{Unconvertable.class,String.class},new Class[]{Sub.class,Super[].class},tc,null);
checkMatch2(new Class[]{Integer.class,Integer.class,String.class},new Class[]{String.class,String.class,Super[].class},tc,null);
checkMatch2(new Class[]{Integer.class,Integer.class,Sub.class},new Class[]{String.class,String.class,Super[].class},tc,ArgsMatchKind.REQUIRES_CONVERSION,0,1);
checkMatch2(new Class[]{Integer.class,Integer.class,Integer.class},new Class[]{Integer.class,String[].class},tc,ArgsMatchKind.REQUIRES_CONVERSION,1,2);
// what happens on (Integer,String) passed to (Integer[]) ?
}
@Test
public void testConvertArguments() throws Exception {
StandardTypeConverter tc = new StandardTypeConverter();
Method oneArg = TestInterface.class.getMethod("oneArg", String.class);
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class);
// basic conversion int>String
Object[] args = new Object[]{3};
ReflectionHelper.convertArguments(tc, args, oneArg, new int[]{0}, null);
checkArguments(args, "3");
// varargs but nothing to convert
args = new Object[]{3};
ReflectionHelper.convertArguments(tc, args, twoArg, new int[]{0}, 1);
checkArguments(args, "3");
// varargs with nothing needing conversion
args = new Object[]{3,"abc","abc"};
ReflectionHelper.convertArguments(tc, args, twoArg, new int[]{0,1,2}, 1);
checkArguments(args, "3","abc","abc");
// varargs with conversion required
args = new Object[]{3,false,3.0d};
ReflectionHelper.convertArguments(tc, args, twoArg, new int[]{0,1,2}, 1);
checkArguments(args, "3","false","3.0");
}
@Test
public void testConvertArguments2() throws Exception {
StandardTypeConverter tc = new StandardTypeConverter();
Method oneArg = TestInterface.class.getMethod("oneArg", String.class);
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class);
// Simple conversion: int to string
Object[] args = new Object[]{3};
ReflectionHelper.convertAllArguments(tc, args, oneArg);
checkArguments(args,"3");
// varargs conversion
args = new Object[]{3,false,3.0f};
ReflectionHelper.convertAllArguments(tc, args, twoArg);
checkArguments(args,"3","false","3.0");
// varargs conversion but no varargs
args = new Object[]{3};
ReflectionHelper.convertAllArguments(tc, args, twoArg);
checkArguments(args,"3");
// missing converter
args = new Object[]{3,false,3.0f};
try {
ReflectionHelper.convertAllArguments(null, args, twoArg);
Assert.fail("Should have failed because no converter supplied");
}
catch (SpelEvaluationException se) {
Assert.assertEquals(SpelMessage.TYPE_CONVERSION_ERROR,se.getMessageCode());
}
// null value
args = new Object[]{3,null,3.0f};
ReflectionHelper.convertAllArguments(tc, args, twoArg);
checkArguments(args,"3",null,"3.0");
}
@Test
public void testSetupArguments() {
Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(new Class[]{new String[0].getClass()},"a","b","c");
Assert.assertEquals(1,newArray.length);
Object firstParam = newArray[0];
Assert.assertEquals(String.class,firstParam.getClass().getComponentType());
Object[] firstParamArray = (Object[])firstParam;
Assert.assertEquals(3,firstParamArray.length);
Assert.assertEquals("a",firstParamArray[0]);
Assert.assertEquals("b",firstParamArray[1]);
Assert.assertEquals("c",firstParamArray[2]);
}
@Test
public void testReflectivePropertyResolver() throws Exception {
ReflectivePropertyAccessor rpr = new ReflectivePropertyAccessor();
Tester t = new Tester();
t.setProperty("hello");
EvaluationContext ctx = new StandardEvaluationContext(t);
Assert.assertTrue(rpr.canRead(ctx, t, "property"));
Assert.assertEquals("hello",rpr.read(ctx, t, "property").getValue());
Assert.assertEquals("hello",rpr.read(ctx, t, "property").getValue()); // cached accessor used
Assert.assertTrue(rpr.canRead(ctx, t, "field"));
Assert.assertEquals(3,rpr.read(ctx, t, "field").getValue());
Assert.assertEquals(3,rpr.read(ctx, t, "field").getValue()); // cached accessor used
Assert.assertTrue(rpr.canWrite(ctx, t, "property"));
rpr.write(ctx, t, "property","goodbye");
rpr.write(ctx, t, "property","goodbye"); // cached accessor used
Assert.assertTrue(rpr.canWrite(ctx, t, "field"));
rpr.write(ctx, t, "field",12);
rpr.write(ctx, t, "field",12);
// Attempted write as first activity on this field and property to drive testing
// of populating type descriptor cache
rpr.write(ctx,t,"field2",3);
rpr.write(ctx, t, "property2","doodoo");
Assert.assertEquals(3,rpr.read(ctx,t,"field2").getValue());
// Attempted read as first activity on this field and property (no canRead before them)
Assert.assertEquals(0,rpr.read(ctx,t,"field3").getValue());
Assert.assertEquals("doodoo",rpr.read(ctx,t,"property3").getValue());
// Access through is method
// Assert.assertEquals(0,rpr.read(ctx,t,"field3").getValue());
Assert.assertEquals(false,rpr.read(ctx,t,"property4").getValue());
Assert.assertTrue(rpr.canRead(ctx,t,"property4"));
}
@Test
public void testOptimalReflectivePropertyResolver() throws Exception {
ReflectivePropertyAccessor rpr = new ReflectivePropertyAccessor();
Tester t = new Tester();
t.setProperty("hello");
EvaluationContext ctx = new StandardEvaluationContext(t);
// Assert.assertTrue(rpr.canRead(ctx, t, "property"));
// Assert.assertEquals("hello",rpr.read(ctx, t, "property").getValue());
// Assert.assertEquals("hello",rpr.read(ctx, t, "property").getValue()); // cached accessor used
PropertyAccessor optA = rpr.createOptimalAccessor(ctx, t, "property");
Assert.assertTrue(optA.canRead(ctx, t, "property"));
Assert.assertFalse(optA.canRead(ctx, t, "property2"));
try {
optA.canWrite(ctx, t, "property");
Assert.fail();
} catch (UnsupportedOperationException uoe) {
// success
}
try {
optA.canWrite(ctx, t, "property2");
Assert.fail();
} catch (UnsupportedOperationException uoe) {
// success
}
Assert.assertEquals("hello",optA.read(ctx, t, "property").getValue());
Assert.assertEquals("hello",optA.read(ctx, t, "property").getValue()); // cached accessor used
try {
optA.getSpecificTargetClasses();
Assert.fail();
} catch (UnsupportedOperationException uoe) {
// success
}
try {
optA.write(ctx,t,"property",null);
Assert.fail();
} catch (UnsupportedOperationException uoe) {
// success
}
optA = rpr.createOptimalAccessor(ctx, t, "field");
Assert.assertTrue(optA.canRead(ctx, t, "field"));
Assert.assertFalse(optA.canRead(ctx, t, "field2"));
try {
optA.canWrite(ctx, t, "field");
Assert.fail();
} catch (UnsupportedOperationException uoe) {
// success
}
try {
optA.canWrite(ctx, t, "field2");
Assert.fail();
} catch (UnsupportedOperationException uoe) {
// success
}
Assert.assertEquals(3,optA.read(ctx, t, "field").getValue());
Assert.assertEquals(3,optA.read(ctx, t, "field").getValue()); // cached accessor used
try {
optA.getSpecificTargetClasses();
Assert.fail();
} catch (UnsupportedOperationException uoe) {
// success
}
try {
optA.write(ctx,t,"field",null);
Assert.fail();
} catch (UnsupportedOperationException uoe) {
// success
}
}
// test classes
static class Tester {
String property;
public int field = 3;
public int field2;
public int field3 = 0;
String property2;
String property3 = "doodoo";
boolean property4 = false;
public String getProperty() { return property; }
public void setProperty(String value) { property = value; }
public void setProperty2(String value) { property2 = value; }
public String getProperty3() { return property3; }
public boolean isProperty4() { return property4; }
}
static class Super {
}
static class Sub extends Super {
}
static class Unconvertable {}
// ---
/**
* Used to validate the match returned from a compareArguments call.
*/
private void checkMatch(Class[] inputTypes, Class[] expectedTypes, StandardTypeConverter typeConverter,ArgsMatchKind expectedMatchKind,int... argsForConversion) {
ReflectionHelper.ArgumentsMatchInfo matchInfo = ReflectionHelper.compareArguments(getTypeDescriptors(expectedTypes), getTypeDescriptors(inputTypes), typeConverter);
if (expectedMatchKind==null) {
Assert.assertNull("Did not expect them to match in any way", matchInfo);
} else {
Assert.assertNotNull("Should not be a null match", matchInfo);
}
if (expectedMatchKind==ArgsMatchKind.EXACT) {
Assert.assertTrue(matchInfo.isExactMatch());
Assert.assertNull(matchInfo.argsRequiringConversion);
} else if (expectedMatchKind==ArgsMatchKind.CLOSE) {
Assert.assertTrue(matchInfo.isCloseMatch());
Assert.assertNull(matchInfo.argsRequiringConversion);
} else if (expectedMatchKind==ArgsMatchKind.REQUIRES_CONVERSION) {
Assert.assertTrue("expected to be a match requiring conversion, but was "+matchInfo,matchInfo.isMatchRequiringConversion());
if (argsForConversion==null) {
Assert.fail("there are arguments that need conversion");
}
Assert.assertEquals("The array of args that need conversion is different length to that expected",argsForConversion.length, matchInfo.argsRequiringConversion.length);
for (int a=0;a<argsForConversion.length;a++) {
Assert.assertEquals(argsForConversion[a],matchInfo.argsRequiringConversion[a]);
}
}
}
/**
* Used to validate the match returned from a compareArguments call.
*/
private void checkMatch2(Class[] inputTypes, Class[] expectedTypes, StandardTypeConverter typeConverter,ArgsMatchKind expectedMatchKind,int... argsForConversion) {
ReflectionHelper.ArgumentsMatchInfo matchInfo = ReflectionHelper.compareArgumentsVarargs(getTypeDescriptors(expectedTypes), getTypeDescriptors(inputTypes), typeConverter);
if (expectedMatchKind==null) {
Assert.assertNull("Did not expect them to match in any way: "+matchInfo, matchInfo);
} else {
Assert.assertNotNull("Should not be a null match", matchInfo);
}
if (expectedMatchKind==ArgsMatchKind.EXACT) {
Assert.assertTrue(matchInfo.isExactMatch());
Assert.assertNull(matchInfo.argsRequiringConversion);
} else if (expectedMatchKind==ArgsMatchKind.CLOSE) {
Assert.assertTrue(matchInfo.isCloseMatch());
Assert.assertNull(matchInfo.argsRequiringConversion);
} else if (expectedMatchKind==ArgsMatchKind.REQUIRES_CONVERSION) {
Assert.assertTrue("expected to be a match requiring conversion, but was "+matchInfo,matchInfo.isMatchRequiringConversion());
if (argsForConversion==null) {
Assert.fail("there are arguments that need conversion");
}
Assert.assertEquals("The array of args that need conversion is different length to that expected",argsForConversion.length, matchInfo.argsRequiringConversion.length);
for (int a=0;a<argsForConversion.length;a++) {
Assert.assertEquals(argsForConversion[a],matchInfo.argsRequiringConversion[a]);
}
}
}
private void checkArguments(Object[] args, Object... expected) {
Assert.assertEquals(expected.length,args.length);
for (int i=0;i<expected.length;i++) {
checkArgument(expected[i],args[i]);
}
}
private void checkArgument(Object expected, Object actual) {
Assert.assertEquals(expected,actual);
}
private List<TypeDescriptor> getTypeDescriptors(Class... types) {
List<TypeDescriptor> typeDescriptors = new ArrayList<TypeDescriptor>(types.length);
for (Class type : types) {
typeDescriptors.add(TypeDescriptor.valueOf(type));
}
return typeDescriptors;
}
public interface TestInterface {
void oneArg(String arg1);
void twoArg(String arg1, String... arg2);
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2002-2009 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.support;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.OperatorOverloader;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypeLocator;
public class StandardComponentsTests {
@Test
public void testStandardEvaluationContext() {
StandardEvaluationContext context = new StandardEvaluationContext();
Assert.assertNotNull(context.getTypeComparator());
TypeComparator tc = new StandardTypeComparator();
context.setTypeComparator(tc);
Assert.assertEquals(tc,context.getTypeComparator());
TypeLocator tl = new StandardTypeLocator();
context.setTypeLocator(tl);
Assert.assertEquals(tl,context.getTypeLocator());
}
@Test
public void testStandardOperatorOverloader() throws EvaluationException {
OperatorOverloader oo = new StandardOperatorOverloader();
Assert.assertFalse(oo.overridesOperation(Operation.ADD, null, null));
try {
oo.operate(Operation.ADD, 2, 3);
Assert.fail("should have failed");
} catch (EvaluationException e) {
// success
}
}
@Test
public void testStandardTypeLocator() {
StandardTypeLocator tl = new StandardTypeLocator();
List<String> prefixes = tl.getImportPrefixes();
Assert.assertEquals(1,prefixes.size());
tl.registerImport("java.util");
prefixes = tl.getImportPrefixes();
Assert.assertEquals(2,prefixes.size());
tl.removeImport("java.util");
prefixes = tl.getImportPrefixes();
Assert.assertEquals(1,prefixes.size());
}
@Test
public void testStandardTypeConverter() throws EvaluationException {
TypeConverter tc = new StandardTypeConverter();
tc.convertValue(3, TypeDescriptor.forObject(3), TypeDescriptor.valueOf(Double.class));
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-2009 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.testresources;
/**
* Hold the various kinds of primitive array for access through the test evaluation context.
*
* @author Andy Clement
*/
public class ArrayContainer {
public int[] ints = new int[3];
public long[] longs = new long[3];
public double[] doubles = new double[3];
public byte[] bytes = new byte[3];
public char[] chars = new char[3];
public short[] shorts = new short[3];
public boolean[] booleans = new boolean[3];
public float[] floats = new float[3];
public ArrayContainer() {
// setup some values
ints[0] = 42;
longs[0] = 42L;
doubles[0] = 42.0d;
bytes[0] = 42;
chars[0] = 42;
shorts[0] = 42;
booleans[0] = true;
floats[0] = 42.0f;
}
}

View File

@@ -0,0 +1,17 @@
/**
*
*/
package org.springframework.expression.spel.testresources;
///CLOVER:OFF
public class Company {
String address;
public Company(String string) {
this.address = string;
}
public String getAddress() {
return address;
}
}

View File

@@ -0,0 +1,40 @@
/**
*
*/
package org.springframework.expression.spel.testresources;
import java.awt.Color;
///CLOVER:OFF
public class Fruit {
public String name; // accessible as property field
public Color color; // accessible as property through getter/setter
public String colorName; // accessible as property through getter/setter
public int stringscount = -1;
public Fruit(String name, Color color, String colorName) {
this.name = name;
this.color = color;
this.colorName = colorName;
}
public Color getColor() {
return color;
}
public Fruit(String... strings) {
stringscount = strings.length;
}
public Fruit(int i, String... strings) {
stringscount = i + strings.length;
}
public int stringscount() {
return stringscount;
}
public String toString() {
return "A" + (colorName != null && colorName.startsWith("o") ? "n " : " ") + colorName + " " + name;
}
}

View File

@@ -0,0 +1,205 @@
package org.springframework.expression.spel.testresources;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.util.ObjectUtils;
///CLOVER:OFF
@SuppressWarnings("unused")
public class Inventor {
private String name;
public String _name;
public String _name_;
public String publicName;
private PlaceOfBirth placeOfBirth;
private Date birthdate;
private int sinNumber;
private String nationality;
private String[] inventions;
public String randomField;
public Map<String,String> testMap;
private boolean wonNobelPrize;
private PlaceOfBirth[] placesLived;
private List<PlaceOfBirth> placesLivedList = new ArrayList<PlaceOfBirth>();
public ArrayContainer arrayContainer;
public boolean publicBoolean;
private boolean accessedThroughGetSet;
public List<Integer> listOfInteger = new ArrayList<Integer>();
public List<Boolean> booleanList = new ArrayList<Boolean>();
public Map<String,Boolean> mapOfStringToBoolean = new HashMap<String,Boolean>();
public Map<Integer,String> mapOfNumbersUpToTen = new HashMap<Integer,String>();
public List<Integer> listOfNumbersUpToTen = new ArrayList<Integer>();
public List<Integer> listOneFive = new ArrayList<Integer>();
public String[] stringArrayOfThreeItems = new String[]{"1","2","3"};
private String foo;
public int counter;
public Inventor(String name, Date birthdate, String nationality) {
this.name = name;
this._name = name;
this._name_ = name;
this.birthdate = birthdate;
this.nationality = nationality;
this.arrayContainer = new ArrayContainer();
testMap = new HashMap<String,String>();
testMap.put("monday", "montag");
testMap.put("tuesday", "dienstag");
testMap.put("wednesday", "mittwoch");
testMap.put("thursday", "donnerstag");
testMap.put("friday", "freitag");
testMap.put("saturday", "samstag");
testMap.put("sunday", "sonntag");
listOneFive.add(1);
listOneFive.add(5);
booleanList.add(false);
booleanList.add(false);
listOfNumbersUpToTen.add(1);
listOfNumbersUpToTen.add(2);
listOfNumbersUpToTen.add(3);
listOfNumbersUpToTen.add(4);
listOfNumbersUpToTen.add(5);
listOfNumbersUpToTen.add(6);
listOfNumbersUpToTen.add(7);
listOfNumbersUpToTen.add(8);
listOfNumbersUpToTen.add(9);
listOfNumbersUpToTen.add(10);
mapOfNumbersUpToTen.put(1,"one");
mapOfNumbersUpToTen.put(2,"two");
mapOfNumbersUpToTen.put(3,"three");
mapOfNumbersUpToTen.put(4,"four");
mapOfNumbersUpToTen.put(5,"five");
mapOfNumbersUpToTen.put(6,"six");
mapOfNumbersUpToTen.put(7,"seven");
mapOfNumbersUpToTen.put(8,"eight");
mapOfNumbersUpToTen.put(9,"nine");
mapOfNumbersUpToTen.put(10,"ten");
}
public void setPlaceOfBirth(PlaceOfBirth placeOfBirth2) {
placeOfBirth = placeOfBirth2;
this.placesLived = new PlaceOfBirth[] { placeOfBirth2 };
this.placesLivedList.add(placeOfBirth2);
}
public String[] getInventions() {
return inventions;
}
public void setInventions(String[] inventions) {
this.inventions = inventions;
}
public PlaceOfBirth getPlaceOfBirth() {
return placeOfBirth;
}
public int throwException(int valueIn) throws Exception {
counter++;
if (valueIn==1) {
throw new IllegalArgumentException("IllegalArgumentException for 1");
}
if (valueIn==2) {
throw new RuntimeException("RuntimeException for 2");
}
if (valueIn==4) {
throw new TestException();
}
return valueIn;
}
static class TestException extends Exception {}
public String throwException(PlaceOfBirth pob) {
return pob.getCity();
}
public String getName() {
return name;
}
public boolean getWonNobelPrize() {
return wonNobelPrize;
}
public void setWonNobelPrize(boolean wonNobelPrize) {
this.wonNobelPrize = wonNobelPrize;
}
public PlaceOfBirth[] getPlacesLived() {
return placesLived;
}
public void setPlacesLived(PlaceOfBirth[] placesLived) {
this.placesLived = placesLived;
}
public List<PlaceOfBirth> getPlacesLivedList() {
return placesLivedList;
}
public void setPlacesLivedList(List<PlaceOfBirth> placesLivedList) {
this.placesLivedList = placesLivedList;
}
public String echo(Object o) {
return o.toString();
}
public String sayHelloTo(String person) {
return "hello " + person;
}
public String printDouble(Double d) {
return d.toString();
}
public String printDoubles(double[] d) {
return ObjectUtils.nullSafeToString(d);
}
public List<String> getDoublesAsStringList() {
List<String> result = new ArrayList<String>();
result.add("14.35");
result.add("15.45");
return result;
}
public String joinThreeStrings(String a, String b, String c) {
return a + b + c;
}
public int aVarargsMethod(String... strings) {
if (strings == null)
return 0;
return strings.length;
}
public int aVarargsMethod2(int i, String... strings) {
if (strings == null)
return i;
return strings.length + i;
}
public Inventor(String... strings) {
}
public boolean getSomeProperty() {
return accessedThroughGetSet;
}
public void setSomeProperty(boolean b) {
this.accessedThroughGetSet = b;
}
public Date getBirthdate() { return birthdate;}
public String getFoo() { return foo; }
public void setFoo(String s) { foo = s; }
public String getNationality() { return nationality; }
}

View File

@@ -0,0 +1,28 @@
package org.springframework.expression.spel.testresources;
///CLOVER:OFF
public class Person {
private String privateName;
Company company;
public Person(String name) {
this.privateName = name;
}
public Person(String name, Company company) {
this.privateName = name;
this.company = company;
}
public String getName() {
return privateName;
}
public void setName(String n) {
this.privateName = n;
}
public Company getCompany() {
return company;
}
}

View File

@@ -0,0 +1,45 @@
package org.springframework.expression.spel.testresources;
///CLOVER:OFF
public class PlaceOfBirth {
private String city;
public String Country;
/**
* Keith now has a converter that supports String to X, if X has a ctor that takes a String.
* In order for round tripping to work we need toString() for X to return what it was
* constructed with. This is a bit of a hack because a PlaceOfBirth also encapsulates a
* country - but as it is just a test object, it is ok.
*/
@Override
public String toString() {return city;}
public String getCity() {
return city;
}
public void setCity(String s) {
this.city = s;
}
public PlaceOfBirth(String string) {
this.city=string;
}
public int doubleIt(int i) {
return i*2;
}
public boolean equals(Object o) {
if (!(o instanceof PlaceOfBirth)) {
return false;
}
PlaceOfBirth oPOB = (PlaceOfBirth)o;
return (city.equals(oPOB.city));
}
public int hashCode() {
return city.hashCode();
}
}

View File

@@ -0,0 +1,21 @@
package org.springframework.expression.spel.testresources;
import java.util.List;
public class TestAddress{
private String street;
private List<String> crossStreets;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public List<String> getCrossStreets() {
return crossStreets;
}
public void setCrossStreets(List<String> crossStreets) {
this.crossStreets = crossStreets;
}
}

View File

@@ -0,0 +1,19 @@
package org.springframework.expression.spel.testresources;
public class TestPerson {
private String name;
private TestAddress address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TestAddress getAddress() {
return address;
}
public void setAddress(TestAddress address) {
this.address = address;
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2002-2011 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.mock.env;
import java.util.Properties;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
/**
* Simple {@link PropertySource} implementation for use in testing. Accepts
* a user-provided {@link Properties} object, or if omitted during construction,
* the implementation will initialize its own.
*
* The {@link #setProperty} and {@link #withProperty} methods are exposed for
* convenience, for example:
* <pre>
* {@code
* PropertySource<?> source = new MockPropertySource().withProperty("foo", "bar");
* }
* </pre>
*
* @author Chris Beams
* @since 3.1
*/
public class MockPropertySource extends PropertiesPropertySource {
/**
* {@value} is the default name for {@link MockPropertySource} instances not
* otherwise given an explicit name.
* @see #MockPropertySource()
* @see #MockPropertySource(String)
*/
public static final String MOCK_PROPERTIES_PROPERTY_SOURCE_NAME = "mockProperties";
/**
* Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
* that will maintain its own internal {@link Properties} instance.
*/
public MockPropertySource() {
this(new Properties());
}
/**
* Create a new {@code MockPropertySource} with the given name that will
* maintain its own internal {@link Properties} instance.
* @param name the {@linkplain #getName() name} of the property source
*/
public MockPropertySource(String name) {
this(name, new Properties());
}
/**
* Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
* and backed by the given {@link Properties} object.
* @param properties the properties to use
*/
public MockPropertySource(Properties properties) {
this(MOCK_PROPERTIES_PROPERTY_SOURCE_NAME, properties);
}
/**
* Create a new {@code MockPropertySource} with with the given name and backed by the given
* {@link Properties} object
* @param name the {@linkplain #getName() name} of the property source
* @param properties the properties to use
*/
public MockPropertySource(String name, Properties properties) {
super(name, properties);
}
/**
* Set the given property on the underlying {@link Properties} object.
*/
public void setProperty(String name, Object value) {
this.source.put(name, value);
}
/**
* Convenient synonym for {@link #setProperty} that returns the current instance.
* Useful for method chaining and fluent-style use.
* @return this {@link MockPropertySource} instance
*/
public MockPropertySource withProperty(String name, Object value) {
this.setProperty(name, value);
return this;
}
}

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
</layout>
</appender>
<logger name="org.springframework.beans">
<level value="warn" />
</logger>
<logger name="org.springframework.binding">
<level value="debug" />
</logger>
<!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
</root>
</log4j:configuration>