15 changed files with 991 additions and 160 deletions
@ -0,0 +1,122 @@
@@ -0,0 +1,122 @@
|
||||
/* |
||||
* 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.ast; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.core.convert.TypeDescriptor; |
||||
import org.springframework.expression.EvaluationException; |
||||
import org.springframework.expression.TypedValue; |
||||
import org.springframework.expression.spel.ExpressionState; |
||||
import org.springframework.expression.spel.SpelNode; |
||||
|
||||
/** |
||||
* Represent a list in an expression, e.g. '{1,2,3}' |
||||
* |
||||
* @author Andy Clement |
||||
* @since 3.0.4 |
||||
*/ |
||||
public class InlineList extends SpelNodeImpl { |
||||
|
||||
// if the list is purely literals, it is a constant value and can be computed and cached
|
||||
TypedValue constant = null; // TODO must be immutable list
|
||||
|
||||
public InlineList(int pos, SpelNodeImpl... args) { |
||||
super(pos, args); |
||||
checkIfConstant(); |
||||
} |
||||
|
||||
/** |
||||
* If all the components of the list are constants, or lists that themselves contain constants, then a constant list |
||||
* can be built to represent this node. This will speed up later getValue calls and reduce the amount of garbage |
||||
* created. |
||||
*/ |
||||
private void checkIfConstant() { |
||||
boolean isConstant = true; |
||||
for (int c = 0, max = getChildCount(); c < max; c++) { |
||||
SpelNode child = getChild(c); |
||||
if (!(child instanceof Literal)) { |
||||
if (child instanceof InlineList) { |
||||
InlineList inlineList = (InlineList) child; |
||||
if (!inlineList.isConstant()) { |
||||
isConstant = false; |
||||
} |
||||
} else { |
||||
isConstant = false; |
||||
} |
||||
} |
||||
} |
||||
if (isConstant) { |
||||
List<Object> constantList = new ArrayList<Object>(); |
||||
int childcount = getChildCount(); |
||||
for (int c = 0; c < childcount; c++) { |
||||
SpelNode child = getChild(c); |
||||
if ((child instanceof Literal)) { |
||||
constantList.add(((Literal) child).getLiteralValue().getValue()); |
||||
} else if (child instanceof InlineList) { |
||||
constantList.add(((InlineList) child).getConstantValue()); |
||||
} |
||||
} |
||||
this.constant = new TypedValue(Collections.unmodifiableList(constantList), TypeDescriptor |
||||
.valueOf(List.class)); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException { |
||||
if (constant != null) { |
||||
return constant; |
||||
} else { |
||||
List<Object> returnValue = new ArrayList<Object>(); |
||||
int childcount = getChildCount(); |
||||
for (int c = 0; c < childcount; c++) { |
||||
returnValue.add(getChild(c).getValue(expressionState)); |
||||
} |
||||
return new TypedValue(returnValue, TypeDescriptor.valueOf(List.class)); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String toStringAST() { |
||||
StringBuilder s = new StringBuilder(); |
||||
// string ast matches input string, not the 'toString()' of the resultant collection, which would use []
|
||||
s.append('{'); |
||||
int count = getChildCount(); |
||||
for (int c = 0; c < count; c++) { |
||||
if (c > 0) { |
||||
s.append(','); |
||||
} |
||||
s.append(getChild(c).toStringAST()); |
||||
} |
||||
s.append('}'); |
||||
return s.toString(); |
||||
} |
||||
|
||||
/** |
||||
* @return whether this list is a constant value |
||||
*/ |
||||
public boolean isConstant() { |
||||
return constant != null; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private List<Object> getConstantValue() { |
||||
return (List<Object>) constant.getValue(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,195 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,149 @@
@@ -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
|
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue