From cfc65b0cc76061224324a6215624d042c130d9b0 Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Wed, 8 Apr 2009 04:50:43 +0000 Subject: [PATCH] Removed unnecessary code. Increased test coverage from 70>75% - still some way to go --- .../expression/TypedValue.java | 8 +- .../expression/spel/ast/Assign.java | 6 - .../spel/ast/ConstructorReference.java | 213 +++--------------- .../expression/spel/ast/FormatHelper.java | 12 +- .../spel/ast/FunctionReference.java | 4 - .../expression/spel/ast/Literal.java | 9 +- .../expression/spel/ast/MethodReference.java | 10 +- .../expression/spel/ast/Operator.java | 20 +- .../expression/spel/ast/OperatorNot.java | 5 - .../expression/spel/ast/OperatorPlus.java | 15 +- .../expression/spel/ast/Placeholder.java | 48 ---- .../expression/spel/ast/Projection.java | 9 +- .../spel/ast/PropertyOrFieldReference.java | 1 + .../spel/ast/QualifiedIdentifier.java | 11 +- .../expression/spel/ast/Selection.java | 22 +- .../expression/spel/ast/SpelNodeImpl.java | 1 + .../expression/spel/ast/SpelTreeAdaptor.java | 8 +- .../expression/spel/ast/Ternary.java | 6 - .../expression/spel/ast/TypeReference.java | 6 - .../support/ReflectiveMethodExecutor.java | 3 +- .../spel/support/StandardTypeConverter.java | 25 ++ .../expression/spel/EvaluationTests.java | 104 +++++++-- .../expression/spel/HelperTests.java | 40 ++++ .../expression/spel/LiteralTests.java | 11 + .../expression/spel/OperatorTests.java | 40 ++-- .../spel/ParserErrorMessagesTests.java | 1 + .../spel/VariableAndFunctionTests.java | 10 +- .../spel/testresources/Company.java | 1 + .../expression/spel/testresources/Fruit.java | 1 + .../spel/testresources/Inventor.java | 23 ++ .../expression/spel/testresources/Person.java | 2 +- .../spel/testresources/PlaceOfBirth.java | 1 + org.springframework.expression/template.mf | 1 + 33 files changed, 287 insertions(+), 390 deletions(-) delete mode 100644 org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Placeholder.java create mode 100644 org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java b/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java index 79d7f6ed1b..00c56fce36 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java @@ -57,5 +57,11 @@ public class TypedValue { public TypeDescriptor getTypeDescriptor() { return this.typeDescriptor; } - + + @Override + public String toString() { + StringBuffer str = new StringBuffer(); + str.append("TypedValue: ").append(value).append(" of type "+typeDescriptor.asString()); + return str.toString(); + } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Assign.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Assign.java index 95086aa2ae..5afb0adb72 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Assign.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Assign.java @@ -20,7 +20,6 @@ import org.antlr.runtime.Token; import org.springframework.expression.EvaluationException; import org.springframework.expression.TypedValue; import org.springframework.expression.spel.ExpressionState; -import org.springframework.expression.spel.SpelException; /** * Represents assignment. An alternative to calling setValue() for an expression is to use an assign. @@ -49,9 +48,4 @@ public class Assign extends SpelNodeImpl { .toString(); } - @Override - public boolean isWritable(ExpressionState expressionState) throws SpelException { - return false; - } - } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java index 0fbe46ed7a..dc4aab2b90 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java @@ -16,11 +16,9 @@ package org.springframework.expression.spel.ast; -import java.lang.reflect.Array; import java.util.List; import org.antlr.runtime.Token; -import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.AccessException; import org.springframework.expression.ConstructorExecutor; import org.springframework.expression.ConstructorResolver; @@ -31,6 +29,7 @@ import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; +// TODO asc array constructor call logic has been removed for now /** * Represents the invocation of a constructor. Either a constructor on a regular type or construction of an array. When * an array is constructed, an initializer can be specified. @@ -46,201 +45,22 @@ import org.springframework.expression.spel.SpelMessages; */ public class ConstructorReference extends SpelNodeImpl { - /** - * If true then this is an array constructor, for example, 'new String[]', rather than a simple constructor 'new - * String()' - */ - private final boolean isArrayConstructor; - + // TODO is this caching safe - passing the expression around will mean this executor is also being passed around /** * The cached executor that may be reused on subsequent evaluations. */ private ConstructorExecutor cachedExecutor; - - public ConstructorReference(Token payload, boolean isArrayConstructor) { + public ConstructorReference(Token payload) { super(payload); - this.isArrayConstructor = isArrayConstructor; } - /** * Implements getValue() - delegating to the code for building an array or a simple type. */ @Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { - if (this.isArrayConstructor) { - return createArray(state); - } - else { - return createNewInstance(state); - } - } - - @Override - public String toStringAST() { - StringBuilder sb = new StringBuilder(); - sb.append("new "); - - int index = 0; - sb.append(getChild(index++).toStringAST()); - - if (!this.isArrayConstructor) { - sb.append("("); - for (int i = index; i < getChildCount(); i++) { - if (i > index) - sb.append(","); - sb.append(getChild(i).toStringAST()); - } - sb.append(")"); - } - else { - // Next child is EXPRESSIONLIST token with children that are the - // expressions giving array size - sb.append("["); - SpelNodeImpl arrayRank = getChild(index++); - for (int i = 0; i < arrayRank.getChildCount(); i++) { - if (i > 0) - sb.append(","); - sb.append(arrayRank.getChild(i).toStringAST()); - } - sb.append("]"); - if (index < getChildCount()) - sb.append(" ").append(getChild(index).toStringAST()); - } - return sb.toString(); - } - - @Override - public boolean isWritable(ExpressionState expressionState) throws SpelException { - return false; - } - - - /** - * Create an array and return it. The children of this node indicate the type of array, the array ranks and any - * optional initializer that might have been supplied. - * @param state the expression state within which this expression is being evaluated - * @return the new array - * @throws EvaluationException if there is a problem creating the array - */ - private TypedValue createArray(ExpressionState state) throws EvaluationException { - TypedValue intendedArrayType = getChild(0).getValueInternal(state); - if (!(intendedArrayType.getValue() instanceof String)) { - throw new SpelException(getChild(0).getCharPositionInLine(), - SpelMessages.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION, - FormatHelper.formatClassNameForMessage(intendedArrayType.getClass())); - } - String type = (String) intendedArrayType.getValue(); - Class componentType = null; - TypeCode arrayTypeCode = TypeCode.forName(type); - if (arrayTypeCode == TypeCode.OBJECT) { - componentType = state.findType(type); - } else { - componentType = arrayTypeCode.getType(); - } - - Object newArray = null; - TypeDescriptor newArrayTypeDescriptor = null; - - if (getChild(1).getChildCount() == 0) { // are the array ranks defined? - if (getChildCount() < 3) { - throw new SpelException(getCharPositionInLine(), - SpelMessages.NO_SIZE_OR_INITIALIZER_FOR_ARRAY_CONSTRUCTION); - } - // no array ranks so use the size of the initializer to determine array size - int arraySize = getChild(2).getChildCount(); - newArray = Array.newInstance(componentType, arraySize); - newArrayTypeDescriptor = TypeDescriptor.valueOf(newArray.getClass()); - } - else { - // Array ranks are specified but is it a single or multiple dimension array? - int dimensions = getChild(1).getChildCount(); - if (dimensions == 1) { - Object o = getChild(1).getValueInternal(state); - int arraySize = state.convertValue(o, INTEGER_TYPE_DESCRIPTOR); - if (getChildCount() == 3) { - // Check initializer length matches array size length - int initializerLength = getChild(2).getChildCount(); - if (initializerLength != arraySize) { - throw new SpelException(getChild(2).getCharPositionInLine(), - SpelMessages.INITIALIZER_LENGTH_INCORRECT, initializerLength, arraySize); - } - } - newArray = Array.newInstance(componentType, arraySize); - newArrayTypeDescriptor = TypeDescriptor.valueOf(newArray.getClass()); - } - else { - // Multi-dimensional - hold onto your hat ! - int[] dims = new int[dimensions]; - for (int d = 0; d < dimensions; d++) { - dims[d] = state.convertValue(getChild(1).getChild(d).getValueInternal(state), INTEGER_TYPE_DESCRIPTOR); - } - newArray = Array.newInstance(componentType, dims); - newArrayTypeDescriptor = TypeDescriptor.valueOf(newArray.getClass()); - // TODO check any specified initializer for the multidim array matches - } - } - - // Populate the array using the initializer if one is specified - if (getChildCount() == 3) { - SpelNodeImpl initializer = getChild(2); - if (arrayTypeCode == TypeCode.OBJECT) { - Object[] newObjectArray = (Object[]) newArray; - for (int i = 0; i < newObjectArray.length; i++) { - SpelNodeImpl elementNode = initializer.getChild(i); - Object arrayEntry = elementNode.getValueInternal(state); - if (!componentType.isAssignableFrom(arrayEntry.getClass())) { - throw new SpelException(elementNode.getCharPositionInLine(), - SpelMessages.INCORRECT_ELEMENT_TYPE_FOR_ARRAY, componentType.getName(), arrayEntry - .getClass().getName()); - } - newObjectArray[i] = arrayEntry; - } - } else if (arrayTypeCode == TypeCode.INT) { - int[] newIntArray = (int[]) newArray; - for (int i = 0; i < newIntArray.length; i++) { - newIntArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), INTEGER_TYPE_DESCRIPTOR); - } - } else if (arrayTypeCode == TypeCode.BOOLEAN) { - boolean[] newBooleanArray = (boolean[]) newArray; - for (int i = 0; i < newBooleanArray.length; i++) { - newBooleanArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR); - } - } else if (arrayTypeCode == TypeCode.CHAR) { - char[] newCharArray = (char[]) newArray; - for (int i = 0; i < newCharArray.length; i++) { - newCharArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), CHARACTER_TYPE_DESCRIPTOR); - } - } else if (arrayTypeCode == TypeCode.SHORT) { - short[] newShortArray = (short[]) newArray; - for (int i = 0; i < newShortArray.length; i++) { - newShortArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), SHORT_TYPE_DESCRIPTOR); - } - } else if (arrayTypeCode == TypeCode.LONG) { - long[] newLongArray = (long[]) newArray; - for (int i = 0; i < newLongArray.length; i++) { - newLongArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), LONG_TYPE_DESCRIPTOR); - } - } else if (arrayTypeCode == TypeCode.FLOAT) { - float[] newFloatArray = (float[]) newArray; - for (int i = 0; i < newFloatArray.length; i++) { - newFloatArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), FLOAT_TYPE_DESCRIPTOR); - } - } else if (arrayTypeCode == TypeCode.DOUBLE) { - double[] newDoubleArray = (double[]) newArray; - for (int i = 0; i < newDoubleArray.length; i++) { - newDoubleArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), DOUBLE_TYPE_DESCRIPTOR); - } - } else if (arrayTypeCode == TypeCode.BYTE) { - byte[] newByteArray = (byte[]) newArray; - for (int i = 0; i < newByteArray.length; i++) { - newByteArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), BYTE_TYPE_DESCRIPTOR); - } - } - } - - return new TypedValue(newArray, newArrayTypeDescriptor); + return createNewInstance(state); } /** @@ -274,9 +94,10 @@ public class ConstructorReference extends SpelNodeImpl { String typename = (String) getChild(0).getValueInternal(state).getValue(); executorToUse = findExecutorForConstructor(typename, argumentTypes, state); try { - return executorToUse.execute(state.getEvaluationContext(), arguments); - } - catch (AccessException ae) { + this.cachedExecutor = executorToUse; + TypedValue result = executorToUse.execute(state.getEvaluationContext(), arguments); + return result; + } catch (AccessException ae) { throw new SpelException(ae, SpelMessages.EXCEPTION_DURING_CONSTRUCTOR_INVOCATION, typename, ae.getMessage()); } } @@ -314,4 +135,22 @@ public class ConstructorReference extends SpelNodeImpl { argumentTypes)); } + @Override + public String toStringAST() { + StringBuilder sb = new StringBuilder(); + sb.append("new "); + + int index = 0; + sb.append(getChild(index++).toStringAST()); + + sb.append("("); + for (int i = index; i < getChildCount(); i++) { + if (i > index) + sb.append(","); + sb.append(getChild(i).toStringAST()); + } + sb.append(")"); + return sb.toString(); + } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java index a0b0eea3c3..ac9bf742b7 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java @@ -21,7 +21,7 @@ package org.springframework.expression.spel.ast; * * @author Andy Clement */ -class FormatHelper { +public class FormatHelper { /** * Produce a nice string for a given method name with specified arguments. @@ -33,13 +33,11 @@ class FormatHelper { StringBuilder sb = new StringBuilder(); sb.append(name); sb.append("("); - if (argumentTypes != null) { - for (int i = 0; i < argumentTypes.length; i++) { - if (i > 0) { - sb.append(","); - } - sb.append(argumentTypes[i].getName()); + for (int i = 0; i < argumentTypes.length; i++) { + if (i > 0) { + sb.append(","); } + sb.append(formatClassNameForMessage(argumentTypes[i])); } sb.append(")"); return sb.toString(); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java index f59e1925cd..4811246750 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java @@ -128,10 +128,6 @@ public class FunctionReference extends SpelNodeImpl { } // to 'assign' to a function don't use the () suffix and so it is just a variable reference - @Override - public boolean isWritable(ExpressionState expressionState) throws EvaluationException { - return false; - } /** * Compute the arguments to the function, they are the children of this expression node. diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Literal.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Literal.java index 792dc4259b..d24bdc0fe9 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Literal.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Literal.java @@ -52,11 +52,6 @@ public abstract class Literal extends SpelNodeImpl { return toString(); } - @Override - public boolean isWritable(ExpressionState expressionState) throws SpelException { - return false; - } - /** * Process the string form of a number, using the specified base if supplied and return an appropriate literal to * hold it. Any suffix to indicate a long will be taken into account (either 'l' or 'L' is supported). @@ -71,9 +66,7 @@ public abstract class Literal extends SpelNodeImpl { boolean isLong = false; boolean isHex = (radix == 16); - if (numberString.length() > 0) { - isLong = numberString.endsWith("L") || numberString.endsWith("l"); - } + isLong = numberString.endsWith("L") || numberString.endsWith("l"); if (isLong || isHex) { // needs to be chopped up a little int len = numberString.length(); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java index 6ea2dc740a..8725b2d402 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java @@ -78,10 +78,9 @@ public class MethodReference extends SpelNodeImpl { try { return executorToUse.execute( state.getEvaluationContext(), state.getActiveContextObject().getValue(), arguments); - } - catch (AccessException ae) { + } catch (AccessException ae) { throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_METHOD_INVOCATION, - this.name, state.getActiveContextObject().getClass().getName(), ae.getMessage()); + this.name, state.getActiveContextObject().getValue().getClass().getName(), ae.getMessage()); } } @@ -130,11 +129,6 @@ public class MethodReference extends SpelNodeImpl { return sb.toString(); } - @Override - public boolean isWritable(ExpressionState expressionState) throws SpelException { - return false; - } - protected MethodExecutor findAccessorForMethod(String name, Class[] argumentTypes, ExpressionState state) throws SpelException { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Operator.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Operator.java index efa06f7fe8..670bfb8b38 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Operator.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Operator.java @@ -17,8 +17,6 @@ package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; -import org.springframework.expression.spel.SpelException; -import org.springframework.expression.spel.ExpressionState; /** * Common supertype for operators that operate on either one or two operands. In the case of multiply or divide there @@ -32,15 +30,7 @@ public abstract class Operator extends SpelNodeImpl { public Operator(Token payload) { super(payload); } - - /** - * Operator expressions can never be written to - */ - @Override - public final boolean isWritable(ExpressionState expressionState) throws SpelException { - return false; - } - + public SpelNodeImpl getLeftOperand() { return getChild(0); } @@ -57,17 +47,13 @@ public abstract class Operator extends SpelNodeImpl { @Override public String toStringAST() { StringBuilder sb = new StringBuilder(); - if (getChildCount() > 0) { - sb.append("("); - } + sb.append("("); sb.append(getChild(0).toStringAST()); for (int i = 1; i < getChildCount(); i++) { sb.append(" ").append(getOperatorName()).append(" "); sb.append(getChild(i).toStringAST()); } - if (getChildCount() > 0) { - sb.append(")"); - } + sb.append(")"); return sb.toString(); } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java index 5c7b33ae1b..574713b3e9 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java @@ -53,9 +53,4 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do return sb.toString(); } - @Override - public boolean isWritable(ExpressionState expressionState) throws SpelException { - return false; - } - } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPlus.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPlus.java index 51160516d1..59f22e2b75 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPlus.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPlus.java @@ -28,11 +28,10 @@ import org.springframework.expression.spel.ExpressionState; *
  • add doubles (floats are represented as doubles) *
  • add longs *
  • add integers - *
  • add a string of one character and a number (effectively increasing that character), so 'a'+3='d' + *
  • concatenate strings * * It can be used as a unary operator for numbers (double/long/int). The standard promotions are performed - * when the operand types vary (double+int=double). - * For other options it defers to the registered overloader. + * when the operand types vary (double+int=double). For other options it defers to the registered overloader. * * @author Andy Clement * @since 3.0 @@ -75,16 +74,6 @@ public class OperatorPlus extends Operator { } } else if (operandOne instanceof String && operandTwo instanceof String) { return new TypedValue(new StringBuilder((String) operandOne).append((String) operandTwo).toString(),STRING_TYPE_DESCRIPTOR); - } else if (operandOne instanceof String && operandTwo instanceof Integer && ((String)operandOne).length()==1) { - String theString = (String) operandOne; - Integer theInteger = (Integer) operandTwo; - // implements character + int (ie. a + 1 = b) - return new TypedValue(Character.toString((char) (theString.charAt(0) + theInteger)),STRING_TYPE_DESCRIPTOR); - } else if (operandOne instanceof Integer && ((operandTwo instanceof String) && ((String)operandTwo).length()==1)) { - String theString = (String) operandTwo; - Integer theInteger = (Integer) operandOne; - // implements character + int (ie. 1 + a = b) - return new TypedValue(Character.toString((char) (theString.charAt(0) + theInteger)),STRING_TYPE_DESCRIPTOR); } return state.operate(Operation.ADD, operandOne, operandTwo); } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Placeholder.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Placeholder.java deleted file mode 100644 index ea04f26748..0000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Placeholder.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.ast; - -import org.antlr.runtime.Token; -import org.springframework.expression.TypedValue; -import org.springframework.expression.spel.ExpressionState; -import org.springframework.expression.spel.SpelException; -import org.springframework.expression.spel.SpelMessages; - -/** - * PlaceHolder nodes are created for tokens that come through from the grammar purely to give us additional positional - * information for messages/etc. - * - * @author Andy Clement - * @since 3.0 - */ -public class Placeholder extends SpelNodeImpl { - - public Placeholder(Token payload) { - super(payload); - } - - @Override - public TypedValue getValueInternal(ExpressionState state) throws SpelException { - throw new SpelException(getCharPositionInLine(), SpelMessages.PLACEHOLDER_SHOULD_NEVER_BE_EVALUATED); - } - - @Override - public String toStringAST() { - return getText(); - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java index d0ff02cb22..18b7190003 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java @@ -61,7 +61,7 @@ public class Projection extends SpelNodeImpl { for (Object k : mapdata.keySet()) { try { state.pushActiveContextObject(new TypedValue(new KeyValuePair(k, mapdata.get(k)),TypeDescriptor.valueOf(KeyValuePair.class))); - result.add(getChild(0).getValueInternal(state)); + result.add(getChild(0).getValueInternal(state).getValue()); } finally { state.popActiveContextObject(); } @@ -76,7 +76,7 @@ public class Projection extends SpelNodeImpl { try { state.pushActiveContextObject(new TypedValue(element,TypeDescriptor.valueOf(op.getTypeDescriptor().getType()))); state.enterScope("index", idx); - result.add(getChild(0).getValueInternal(state)); + result.add(getChild(0).getValueInternal(state).getValue()); } finally { state.exitScope(); state.popActiveContextObject(); @@ -95,9 +95,4 @@ public class Projection extends SpelNodeImpl { return sb.append("!{").append(getChild(0).toStringAST()).append("}").toString(); } - @Override - public boolean isWritable(ExpressionState expressionState) throws SpelException { - return false; - } - } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java index 48c0f809f9..cb04fa4a13 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java @@ -145,6 +145,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } } } catch (AccessException ae) { + ae.printStackTrace(); throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_PROPERTY_WRITE, name, ae.getMessage()); } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java index 7a6740a59f..fbdf6fd9f0 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java @@ -31,7 +31,7 @@ import org.springframework.expression.spel.ExpressionState; */ public class QualifiedIdentifier extends SpelNodeImpl { - private String value; + private TypedValue value; public QualifiedIdentifier(Token payload) { super(payload); @@ -49,18 +49,17 @@ public class QualifiedIdentifier extends SpelNodeImpl { } sb.append(getChild(i).getValueInternal(state).getValue()); } - this.value = sb.toString(); + this.value = new TypedValue(sb.toString(),STRING_TYPE_DESCRIPTOR); } - return new TypedValue(this.value,STRING_TYPE_DESCRIPTOR); + return this.value; } @Override public String toStringAST() { StringBuilder sb = new StringBuilder(); if (this.value != null) { - sb.append(this.value); - } - else { + sb.append(this.value.getValue()); + } else { for (int i = 0; i < getChildCount(); i++) { if (i > 0) { sb.append("."); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java index 325b2179f6..a7cdbdc0db 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java @@ -18,6 +18,7 @@ package org.springframework.expression.spel.ast; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -59,17 +60,21 @@ public class Selection extends SpelNodeImpl { SpelNodeImpl selectionCriteria = getChild(0); if (operand instanceof Map) { Map mapdata = (Map) operand; - List result = new ArrayList(); + // TODO don't lose generic info for the new map + Map result = new HashMap(); for (Object k : mapdata.keySet()) { try { - TypedValue kvpair = new TypedValue(new KeyValuePair(k, mapdata.get(k)),TypeDescriptor.valueOf(KeyValuePair.class)); + KeyValuePair kvp = new KeyValuePair(k,mapdata.get(k)); + TypedValue kvpair = new TypedValue(kvp,TypeDescriptor.valueOf(KeyValuePair.class)); state.pushActiveContextObject(kvpair); - Object o = selectionCriteria.getValueInternal(state); + Object o = selectionCriteria.getValueInternal(state).getValue(); if (o instanceof Boolean) { if (((Boolean) o).booleanValue() == true) { - if (variant == FIRST) - return kvpair; - result.add(kvpair); + if (variant == FIRST) { + result.put(kvp.key,kvp.value); + return new TypedValue(result); + } + result.put(kvp.key,kvp.value); } } else { throw new SpelException(selectionCriteria.getCharPositionInLine(), @@ -142,9 +147,4 @@ public class Selection extends SpelNodeImpl { return sb.append(getChild(0).toStringAST()).append("}").toString(); } - @Override - public boolean isWritable(ExpressionState expressionState) throws SpelException { - return false; - } - } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java index 4b3c9df5c2..218846a094 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java @@ -55,6 +55,7 @@ public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Seria } } + // by default Ast nodes are not writable public boolean isWritable(ExpressionState expressionState) throws EvaluationException { return false; } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java index 74c3ef5714..623779990d 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java @@ -18,7 +18,6 @@ package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; import org.antlr.runtime.tree.CommonTreeAdaptor; - import org.springframework.expression.spel.generated.SpringExpressionsLexer; /** @@ -95,7 +94,7 @@ public class SpelTreeAdaptor extends CommonTreeAdaptor { return new CompoundExpression(payload); case SpringExpressionsLexer.CONSTRUCTOR: - return new ConstructorReference(payload, false); + return new ConstructorReference(payload); case SpringExpressionsLexer.VARIABLEREF: return new VariableReference(payload); case SpringExpressionsLexer.FUNCTIONREF: @@ -123,11 +122,6 @@ public class SpelTreeAdaptor extends CommonTreeAdaptor { case SpringExpressionsLexer.INSTANCEOF: return new OperatorInstanceof(payload); - case SpringExpressionsLexer.RPAREN: - return new Placeholder(payload); - case SpringExpressionsLexer.COLON: - return new Placeholder(payload); - case SpringExpressionsLexer.DOT: return new Dot(payload); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java index 0989313aab..2f85863367 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java @@ -20,7 +20,6 @@ import org.antlr.runtime.Token; import org.springframework.expression.EvaluationException; import org.springframework.expression.TypedValue; import org.springframework.expression.spel.ExpressionState; -import org.springframework.expression.spel.SpelException; /** * Represents a ternary expression, for example: "someCheck()?true:false". @@ -57,9 +56,4 @@ public class Ternary extends SpelNodeImpl { .append(" : ").append(getChild(2).toStringAST()).toString(); } - @Override - public boolean isWritable(ExpressionState expressionState) throws SpelException { - return false; - } - } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java index 7fbafed924..999ee7c0ed 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java @@ -20,7 +20,6 @@ import org.antlr.runtime.Token; import org.springframework.expression.EvaluationException; import org.springframework.expression.TypedValue; import org.springframework.expression.spel.ExpressionState; -import org.springframework.expression.spel.SpelException; /** * Represents a reference to a type, for example "T(String)" or "T(com.somewhere.Foo)" @@ -56,9 +55,4 @@ public class TypeReference extends SpelNodeImpl { return sb.toString(); } - @Override - public boolean isWritable(ExpressionState expressionState) throws SpelException { - return false; - } - } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java index 3f601dc753..4cdf47346a 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java @@ -56,8 +56,7 @@ class ReflectiveMethodExecutor implements MethodExecutor { } ReflectionUtils.makeAccessible(this.method); return new TypedValue(this.method.invoke(target, arguments), new TypeDescriptor(new MethodParameter(method,-1))); - } - catch (Exception ex) { + } catch (Exception ex) { throw new AccessException("Problem invoking method: " + this.method, ex); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java index 7ecb75941b..e6baf7d043 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java @@ -17,6 +17,7 @@ package org.springframework.expression.spel.support; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.service.DefaultConversionService; import org.springframework.expression.EvaluationException; import org.springframework.expression.TypeConverter; import org.springframework.expression.spel.SpelException; @@ -31,8 +32,16 @@ import org.springframework.util.NumberUtils; */ public class StandardTypeConverter implements TypeConverter { + DefaultConversionService conversionService; + + StandardTypeConverter() { + conversionService = new DefaultConversionService(); + } + @SuppressWarnings("unchecked") public T convertValue(Object value, Class targetType) throws EvaluationException { + // For activation when conversion service available - this replaces the rest of the method (probably...) + // return (T)convertValue(value,TypeDescriptor.valueOf(targetType)); if (ClassUtils.isAssignableValue(targetType, value)) { return (T) value; } @@ -78,10 +87,20 @@ public class StandardTypeConverter implements TypeConverter { @SuppressWarnings("unchecked") public T convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException { +// For activation when conversion service available - this replaces the rest of the method (probably...) +// try { +// return (T)conversionService.executeConversion(value, typeDescriptor); +// } catch (ConversionExecutorNotFoundException cenfe) { +// throw new SpelException(cenfe, SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), typeDescriptor.asString()); +// } catch (ConversionException ce) { +// throw new SpelException(ce, SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), typeDescriptor.asString()); +// } return (T)convertValue(value,typeDescriptor.getType()); } public boolean canConvert(Class sourceType, Class targetType) { + // For activation when conversion service available - this replaces the rest of the method (probably...) + // return canConvert(sourceType,TypeDescriptor.valueOf(targetType)); if (ClassUtils.isAssignable(targetType, sourceType) || String.class.equals(targetType)) { return true; } @@ -92,6 +111,12 @@ public class StandardTypeConverter implements TypeConverter { } public boolean canConvert(Class sourceType, TypeDescriptor typeDescriptor) { + // For activation when conversion service available - this replaces the rest of the method (probably...) +// try { +// return conversionService.getConversionExecutor(sourceType, typeDescriptor)!=null; +// } catch (ConversionExecutorNotFoundException cenfe) { +// return false; +// } return canConvert(sourceType,typeDescriptor.getType()); } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index c32ed1de72..973789f59d 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -19,6 +19,7 @@ package org.springframework.expression.spel; import java.util.ArrayList; import org.springframework.expression.Expression; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** * Tests the evaluation of real expressions in a real context. @@ -208,6 +209,22 @@ public class EvaluationTests extends ExpressionTestCase { public void testConstructorInvocation05() { evaluate("new java.lang.String('foobar')", "foobar", String.class); } + + 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); + assertEquals("wibble",newString); + newString = expr.getValue(String.class); + assertEquals("wibble",newString); + + // not writable + assertFalse(expr.isWritable(new StandardEvaluationContext())); + + // ast + assertEquals("new String('wibble')",expr.toStringAST()); + } + // array construction // public void testArrayConstruction01() { @@ -275,14 +292,18 @@ public class EvaluationTests extends ExpressionTestCase { // } // projection and selection -// public void testProjection01() { -// 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); -// } - // - // public void testProjection02() { - // evaluate("#{'a':'y','b':'n','c':'y'}.!{value=='y'?key:null}.nonnull().sort()", "[a, c]", ArrayList.class); - // } - // + 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); + } + + 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); + } + // 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); @@ -291,11 +312,21 @@ public class EvaluationTests extends ExpressionTestCase { // 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); // } - - 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 testProjection05() { + evaluateAndCheckError("'abc'.!{true}", SpelMessages.PROJECTION_NOT_SUPPORTED_ON_TYPE); + } + + public void testProjection06() throws Exception { + SpelExpression expr = (SpelExpression)parser.parseExpression("'abc'.!{true}"); + assertEquals("'abc'.!{true}",expr.toStringAST()); + assertFalse(expr.isWritable(new StandardEvaluationContext())); } + + //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 testSelection02() { evaluate("testMap.keySet().?{#this matches '.*o.*'}", "[monday]", ArrayList.class); @@ -303,23 +334,40 @@ public class EvaluationTests extends ExpressionTestCase { evaluate("testMap.keySet().?{#this matches '.*r.*'}.size()", "3", Integer.class); } - // public void testSelectionError_NonBooleanSelectionCriteria() { - // evaluateAndCheckError("{1,2,3,4,5,6,7,8,9,10}.?{'nonboolean'}", - // SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); - // } + public void testSelectionError_NonBooleanSelectionCriteria() { + evaluateAndCheckError("listOfNumbersUpToTen.?{'nonboolean'}", + SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); + } // public void testSelectionUsingIndex() { // evaluate("{1,2,3,4,5,6,7,8,9,10}.?{$index > 5 }", "[7, 8, 9, 10]", ArrayList.class); // } + + public void testSelection03() { + evaluate("mapOfNumbersUpToTen.?{key>5}.size()", "5", Integer.class); + } - // public void testSelectionFirst01() { - // evaluate("{1,2,3,4,5,6,7,8,9,10}.^{#isEven(#this) == 'y'}", "2", Integer.class); - // } - // - // public void testSelectionLast01() { - // evaluate("{1,2,3,4,5,6,7,8,9,10}.${#isEven(#this) == 'y'}", "10", Integer.class); - // } + public void testSelectionFirst01() { + evaluate("listOfNumbersUpToTen.^{#isEven(#this) == 'y'}", "2", Integer.class); + } + + public void testSelectionLast01() { + evaluate("listOfNumbersUpToTen.${#isEven(#this) == 'y'}", "10", Integer.class); + } + + public void testSelectionAST() throws Exception { + SpelExpression expr = (SpelExpression)parser.parseExpression("'abc'.^{true}"); + assertEquals("'abc'.^{true}",expr.toStringAST()); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + expr = (SpelExpression)parser.parseExpression("'abc'.?{true}"); + assertEquals("'abc'.?{true}",expr.toStringAST()); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + expr = (SpelExpression)parser.parseExpression("'abc'.${true}"); + assertEquals("'abc'.${true}",expr.toStringAST()); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + } + // assignment public void testAssignmentToVariables01() { evaluate("#var1='value1'", "value1", String.class); @@ -451,6 +499,16 @@ public class EvaluationTests extends ExpressionTestCase { public void testTypeReferences01() { evaluate("T(java.lang.String)", "class java.lang.String", Class.class); } + + public void testTypeReferencesAndQualifiedIdentifierCaching() throws Exception { + SpelExpression expr = (SpelExpression)parser.parseExpression("T(java.lang.String)"); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + assertEquals("T(java.lang.String)",expr.toStringAST()); + assertEquals(String.class,expr.getValue(Class.class)); + // use cached QualifiedIdentifier: + assertEquals("T(java.lang.String)",expr.toStringAST()); + assertEquals(String.class,expr.getValue(Class.class)); + } public void testTypeReferencesPrimitive() { evaluate("T(int)", "int", Class.class); diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java new file mode 100644 index 0000000000..c845d50628 --- /dev/null +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java @@ -0,0 +1,40 @@ +/* + * 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.springframework.expression.spel.ast.FormatHelper; + +/** + * Tests for any helper code. + * + * @author Andy Clement + */ +public class HelperTests extends ExpressionTestCase { + + public void testFormatHelperForClassName() { + assertEquals("java.lang.String",FormatHelper.formatClassNameForMessage(String.class)); + assertEquals("java.lang.String[]",FormatHelper.formatClassNameForMessage(new String[1].getClass())); + assertEquals("int[]",FormatHelper.formatClassNameForMessage(new int[1].getClass())); + assertEquals("int[][]",FormatHelper.formatClassNameForMessage(new int[1][2].getClass())); + assertEquals("null",FormatHelper.formatClassNameForMessage(null)); + } + + public void testFormatHelperForMethod() { + assertEquals("foo(java.lang.String)",FormatHelper.formatMethodForMessage("foo", String.class)); + assertEquals("goo(java.lang.String,int[])",FormatHelper.formatMethodForMessage("goo", String.class,new int[1].getClass())); + assertEquals("boo()",FormatHelper.formatMethodForMessage("boo")); + } +} diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java index 8f53428482..93abedd08f 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java @@ -16,6 +16,8 @@ package org.springframework.expression.spel; +import org.springframework.expression.spel.support.StandardEvaluationContext; + /** * Tests the evaluation of basic literals: boolean, integer, hex integer, long, real, null, date * @@ -134,4 +136,13 @@ public class LiteralTests extends ExpressionTestCase { 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 } + + public void testNotWritable() throws Exception { + SpelExpression expr = (SpelExpression)parser.parseExpression("37"); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + expr = (SpelExpression)parser.parseExpression("37L"); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + expr = (SpelExpression)parser.parseExpression("true"); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java index c193e1a309..97439e82bd 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java @@ -63,6 +63,8 @@ public class OperatorTests extends ExpressionTestCase { 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); } @@ -70,6 +72,8 @@ public class OperatorTests extends ExpressionTestCase { 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); } public void testGreaterThanOrEqual() { @@ -122,23 +126,36 @@ public class OperatorTests extends ExpressionTestCase { } public void testPlus() throws Exception { - evaluate("'a' + 2", "c", String.class); + evaluate("7 + 2", "9", Integer.class); + evaluate("3.0f + 5.0f", 8.0d, Double.class); + evaluate("3.0d + 5.0d", 8.0d, Double.class); + evaluateAndCheckError("'ab' + 2", SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); - evaluate("2+'a' ", "c", String.class); + evaluateAndCheckError("2+'a' ", SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); evaluateAndCheckError("2+'ab'",SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); + + // AST: SpelExpression expr = (SpelExpression)parser.parseExpression("+3"); assertEquals("+3",expr.toStringAST()); expr = (SpelExpression)parser.parseExpression("2+3"); 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'",SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); + + // string concatenation + evaluate("'abc'+'def'","abcdef",String.class); + + // + evaluate("5 + new Integer('37')",42,Integer.class); } public void testMinus() throws Exception { evaluate("'c' - 2", "a", String.class); + evaluate("3.0f - 5.0f", -2.0d, Double.class); evaluateAndCheckError("'ab' - 2", SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); evaluateAndCheckError("2-'ab'",SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); SpelExpression expr = (SpelExpression)parser.parseExpression("-3"); @@ -156,11 +173,14 @@ public class OperatorTests extends ExpressionTestCase { evaluate("3%2",1,Integer.class); evaluate("3L%2L",1L,Long.class); evaluate("3.0d%2.0d",1d,Double.class); + evaluate("5.0f % 3.1f", 1.9d, Double.class); evaluateAndCheckError("'abc'%'def'",SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); } public void testDivide() { + evaluate("3.0f / 5.0f", 0.6d, Double.class); evaluate("4L/2L",2L,Long.class); + evaluateAndCheckError("'abc'/'def'",SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES); } public void testMathOperatorDivide_ConvertToDouble() { @@ -182,20 +202,8 @@ public class OperatorTests extends ExpressionTestCase { evaluate("3.0d / 5.0d", 0.6d, Double.class); evaluate("6.0d % 3.5d", 2.5d, Double.class); } - - public void testFloats() { - evaluate("3.0f == 5.0f", false, Boolean.class); - evaluate("3.0f == 3.0f", true, Boolean.class); - evaluate("3.0f != 5.0f", true, Boolean.class); - evaluate("3.0f != 3.0f", false, Boolean.class); - evaluate("3.0f + 5.0f", 8.0d, Double.class); - evaluate("3.0f - 5.0f", -2.0d, Double.class); - evaluate("3.0f * 5.0f", 15.0d, Double.class); - evaluate("3.0f / 5.0f", 0.6d, Double.class); - evaluate("5.0f % 3.1f", 1.9d, Double.class); - } - public void testOperatorStrings() throws Exception { + public void testOperatorNames() throws Exception { Operator node = getOperatorNode((SpelExpression)parser.parseExpression("1==3")); assertEquals("==",node.getOperatorName()); @@ -266,6 +274,8 @@ public class OperatorTests extends ExpressionTestCase { evaluate("3L - 50L", -47L, Long.class); } + // --- + private Operator getOperatorNode(SpelExpression e) { SpelNode node = e.getAST(); return (Operator)findNode(node,Operator.class); diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java index 531fdf6327..2e57a38033 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java @@ -29,6 +29,7 @@ public class ParserErrorMessagesTests extends ExpressionTestCase { // will not fit into an int, needs L suffix parseAndCheckError("0xCAFEBABE", SpelMessages.NOT_AN_INTEGER); evaluate("0xCAFEBABEL", 0xCAFEBABEL, Long.class); + parseAndCheckError("0xCAFEBABECAFEBABEL", SpelMessages.NOT_A_LONG); } public void testBrokenExpression02() { diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java index 15c70dadc1..bb8f4216a0 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java @@ -19,6 +19,7 @@ package org.springframework.expression.spel; import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; + /** * Tests the evaluation of expressions that access variables and functions (lambda/java). * @@ -26,10 +27,15 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; */ public class VariableAndFunctionTests extends ExpressionTestCase { - public void testVariableAccess() { + public void testVariableAccess01() { evaluate("#answer", "42", Integer.class, SHOULD_BE_WRITABLE); evaluate("#answer / 2", 21, Integer.class, SHOULD_NOT_BE_WRITABLE); } + + public void testVariableAccess_WellKnownVariables() { + evaluate("#this.getName()","Nikola Tesla",String.class); + evaluate("#root.getName()","Nikola Tesla",String.class); + } public void testFunctionAccess01() { evaluate("#reverseInt(1,2,3)", "int[3]{3,2,1}", int[].class); @@ -71,7 +77,7 @@ public class VariableAndFunctionTests extends ExpressionTestCase { } } } - + // this method is used by the test above public void nonStatic() { } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Company.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Company.java index fe30c027ce..54a8384ff0 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Company.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Company.java @@ -3,6 +3,7 @@ */ package org.springframework.expression.spel.testresources; +///CLOVER:OFF public class Company { String address; diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Fruit.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Fruit.java index 1daf7d1316..ba3e5c8434 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Fruit.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Fruit.java @@ -5,6 +5,7 @@ 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 diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java index ef878107eb..cf7fd53119 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Inventor.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +///CLOVER:OFF @SuppressWarnings("unused") public class Inventor { private String name; @@ -26,6 +27,8 @@ public class Inventor { public List listOfInteger = new ArrayList(); public List booleanList = new ArrayList(); public Map mapOfStringToBoolean = new HashMap(); + public Map mapOfNumbersUpToTen = new HashMap(); + public List listOfNumbersUpToTen = new ArrayList(); public Inventor(String name, Date birthdate, String nationality) { this.name = name; @@ -42,6 +45,26 @@ public class Inventor { testMap.put("sunday", "sonntag"); 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) { diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Person.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Person.java index 2826f47a96..80a432d256 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Person.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/Person.java @@ -1,6 +1,6 @@ package org.springframework.expression.spel.testresources; - +///CLOVER:OFF public class Person { private String privateName; Company company; diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/PlaceOfBirth.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/PlaceOfBirth.java index 305a14b86b..459b88827c 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/PlaceOfBirth.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/testresources/PlaceOfBirth.java @@ -1,5 +1,6 @@ package org.springframework.expression.spel.testresources; +///CLOVER:OFF public class PlaceOfBirth { private String city; diff --git a/org.springframework.expression/template.mf b/org.springframework.expression/template.mf index 3bc282b38a..32c52c5599 100644 --- a/org.springframework.expression/template.mf +++ b/org.springframework.expression/template.mf @@ -6,6 +6,7 @@ Import-Template: org.springframework.util;version="[3.0.0, 3.0.1)", org.springframework.core;version="[3.0.0, 3.0.1)", org.springframework.core.convert;version="[3.0.0, 3.0.1)", + org.springframework.core.convert.service;version="[3.0.0, 3.0.1)", org.apache.commons.logging;version="[1.1.1, 2.0.0)", org.antlr.runtime;version="[3.0.1,4.0.0)", org.antlr.runtime.tree;version="[3.0.1,4.0.0)"