More tests and some fixes. Also created tests based on the documentation examples.
This commit is contained in:
@@ -36,6 +36,13 @@ public interface EvaluationContext {
|
||||
* @return the root context object against which unqualified properties/methods/etc should be resolved
|
||||
*/
|
||||
TypedValue getRootObject();
|
||||
|
||||
|
||||
/**
|
||||
* @param rootObject the root object against which unqualified properties/methods/etc should be resolved
|
||||
*/
|
||||
void setRootObject(Object object);
|
||||
|
||||
|
||||
/**
|
||||
* Set a named variable within this execution context to a specified value.
|
||||
|
||||
@@ -48,17 +48,11 @@ public class ExpressionState {
|
||||
|
||||
private final Stack<TypedValue> contextObjects = new Stack<TypedValue>();
|
||||
|
||||
|
||||
public ExpressionState() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public ExpressionState(EvaluationContext context) {
|
||||
this.relatedContext = context;
|
||||
createVariableScope();
|
||||
}
|
||||
|
||||
|
||||
private void createVariableScope() {
|
||||
this.variableScopes.add(new VariableScope()); // create an empty top level VariableScope
|
||||
}
|
||||
@@ -95,7 +89,7 @@ public class ExpressionState {
|
||||
|
||||
public TypedValue lookupVariable(String name) {
|
||||
Object value = this.relatedContext.lookupVariable(name);
|
||||
return new TypedValue(value,TypeDescriptor.valueOf((value==null?null:value.getClass())));
|
||||
return new TypedValue(value,TypeDescriptor.forObject(value));
|
||||
}
|
||||
|
||||
public TypeComparator getTypeComparator() {
|
||||
|
||||
@@ -113,10 +113,10 @@ public enum SpelMessages {
|
||||
"The value ''{0}'' cannot be parsed as a long"), PARSE_PROBLEM(Kind.ERROR, 1066,
|
||||
"Error occurred during expression parse: {0}"), INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR,
|
||||
1067, "First operand to matches operator must be a string. ''{0}'' is not"), INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR(
|
||||
Kind.ERROR, 1068, "Second operand to matches operator must be a string. ''{0}'' is not"), FUNCTION_MUST_BE_STATIC(
|
||||
Kind.ERROR,
|
||||
1069,
|
||||
"Only static methods can be called via function references. The method ''{0}'' referred to by name ''{1}'' is not static.");
|
||||
Kind.ERROR, 1068, "Second operand to matches operator must be a string. ''{0}'' is not"), //
|
||||
FUNCTION_MUST_BE_STATIC(Kind.ERROR, 1069,
|
||||
"Only static methods can be called via function references. The method ''{0}'' referred to by name ''{1}'' is not static."),//
|
||||
CANNOT_INDEX_INTO_NULL_VALUE(Kind.ERROR, 1070, "Cannot index into a null value");
|
||||
|
||||
private Kind kind;
|
||||
private int code;
|
||||
|
||||
@@ -74,8 +74,9 @@ public class ConstructorReference extends SpelNodeImpl {
|
||||
Class<?>[] argumentTypes = new Class[getChildCount() - 1];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
TypedValue childValue = getChild(i + 1).getValueInternal(state);
|
||||
arguments[i] = childValue.getValue();
|
||||
argumentTypes[i] = childValue.getValue().getClass();
|
||||
Object value = childValue.getValue();
|
||||
arguments[i] = value;
|
||||
argumentTypes[i] = value==null?Object.class:value.getClass();
|
||||
}
|
||||
|
||||
ConstructorExecutor executorToUse = this.cachedExecutor;
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelException;
|
||||
import org.springframework.expression.spel.SpelMessages;
|
||||
import org.springframework.expression.spel.support.ReflectionHelper;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A function reference is of the form "#someFunction(a,b,c)". Functions may be defined in the context prior to the
|
||||
@@ -101,7 +102,9 @@ public class FunctionReference extends SpelNodeImpl {
|
||||
}
|
||||
|
||||
try {
|
||||
return new TypedValue(m.invoke(m.getClass(), functionArgs),new TypeDescriptor(new MethodParameter(m,-1)));
|
||||
ReflectionUtils.makeAccessible(m);
|
||||
Object result = m.invoke(m.getClass(), functionArgs);
|
||||
return new TypedValue(result, new TypeDescriptor(new MethodParameter(m,-1)));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new SpelException(getCharPositionInLine(), e, SpelMessages.EXCEPTION_DURING_FUNCTION_CALL, name, e
|
||||
.getMessage());
|
||||
|
||||
@@ -60,9 +60,13 @@ public class Indexer extends SpelNodeImpl {
|
||||
|
||||
int idx = state.convertValue(index, INTEGER_TYPE_DESCRIPTOR);
|
||||
|
||||
if (targetObjectTypeDescriptor.isArray()) {
|
||||
if (targetObject == null) {
|
||||
throw new SpelException(SpelMessages.CANNOT_INDEX_INTO_NULL_VALUE);
|
||||
}
|
||||
|
||||
if (targetObject.getClass().isArray()) {
|
||||
return new TypedValue(accessArrayElement(targetObject, idx),TypeDescriptor.valueOf(targetObjectTypeDescriptor.getElementType()));
|
||||
} else if (targetObjectTypeDescriptor.isCollection()) {
|
||||
} else if (targetObject instanceof Collection) {
|
||||
Collection<?> c = (Collection<?>) targetObject;
|
||||
if (idx >= c.size()) {
|
||||
throw new SpelException(SpelMessages.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
|
||||
@@ -81,7 +85,7 @@ public class Indexer extends SpelNodeImpl {
|
||||
}
|
||||
return new TypedValue(String.valueOf(ctxString.charAt(idx)),STRING_TYPE_DESCRIPTOR);
|
||||
}
|
||||
throw new SpelException(SpelMessages.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetObjectTypeDescriptor);
|
||||
throw new SpelException(SpelMessages.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetObjectTypeDescriptor.asString());
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +102,9 @@ public class Indexer extends SpelNodeImpl {
|
||||
TypeDescriptor targetObjectTypeDescriptor = contextObject.getTypeDescriptor();
|
||||
TypedValue index = getChild(0).getValueInternal(state);
|
||||
|
||||
if (targetObject == null) {
|
||||
throw new SpelException(SpelMessages.CANNOT_INDEX_INTO_NULL_VALUE);
|
||||
}
|
||||
// Indexing into a Map
|
||||
if (targetObjectTypeDescriptor.isMap()) {
|
||||
Map map = (Map)targetObject;
|
||||
|
||||
@@ -54,9 +54,9 @@ public class MethodReference extends SpelNodeImpl {
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
arguments[i] = getChild(i).getValueInternal(state).getValue();
|
||||
}
|
||||
if (currentContext == null) {
|
||||
if (currentContext.getValue() == null) {
|
||||
throw new SpelException(getCharPositionInLine(), SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT,
|
||||
formatMethodForMessage(name, getTypes(arguments)));
|
||||
FormatHelper.formatMethodForMessage(name, getTypes(arguments)));
|
||||
}
|
||||
|
||||
MethodExecutor executorToUse = this.cachedExecutor;
|
||||
@@ -85,12 +85,9 @@ public class MethodReference extends SpelNodeImpl {
|
||||
}
|
||||
|
||||
private Class<?>[] getTypes(Object... arguments) {
|
||||
if (arguments == null) {
|
||||
return null;
|
||||
}
|
||||
Class<?>[] argumentTypes = new Class[arguments.length];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
argumentTypes[i] = arguments[i].getClass();
|
||||
argumentTypes[i] = (arguments[i]==null?Object.class:arguments[i].getClass());
|
||||
}
|
||||
return argumentTypes;
|
||||
}
|
||||
@@ -108,38 +105,13 @@ public class MethodReference extends SpelNodeImpl {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a nice string for a given method name with specified arguments.
|
||||
* @param name the name of the method
|
||||
* @param argumentTypes the types of the arguments to the method
|
||||
* @return nicely formatted string, eg. foo(String,int)
|
||||
*/
|
||||
private String formatMethodForMessage(String name, Class<?>... argumentTypes) {
|
||||
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].getClass());
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
protected MethodExecutor findAccessorForMethod(String name, Class<?>[] argumentTypes, ExpressionState state)
|
||||
throws SpelException {
|
||||
|
||||
TypedValue context = state.getActiveContextObject();
|
||||
Object contextObject = context.getValue();
|
||||
EvaluationContext eContext = state.getEvaluationContext();
|
||||
if (contextObject == null) {
|
||||
throw new SpelException(SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT,
|
||||
FormatHelper.formatMethodForMessage(name, argumentTypes));
|
||||
}
|
||||
|
||||
List<MethodResolver> mResolvers = eContext.getMethodResolvers();
|
||||
if (mResolvers != null) {
|
||||
for (MethodResolver methodResolver : mResolvers) {
|
||||
|
||||
@@ -145,7 +145,6 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
}
|
||||
}
|
||||
} catch (AccessException ae) {
|
||||
ae.printStackTrace();
|
||||
throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_PROPERTY_WRITE,
|
||||
name, ae.getMessage());
|
||||
}
|
||||
|
||||
@@ -62,8 +62,10 @@ public class Selection extends SpelNodeImpl {
|
||||
Map<?, ?> mapdata = (Map<?, ?>) operand;
|
||||
// TODO don't lose generic info for the new map
|
||||
Map<Object,Object> result = new HashMap<Object,Object>();
|
||||
Object lastKey = null;
|
||||
for (Object k : mapdata.keySet()) {
|
||||
try {
|
||||
lastKey = k;
|
||||
KeyValuePair kvp = new KeyValuePair(k,mapdata.get(k));
|
||||
TypedValue kvpair = new TypedValue(kvp,TypeDescriptor.valueOf(KeyValuePair.class));
|
||||
state.pushActiveContextObject(kvpair);
|
||||
@@ -83,12 +85,15 @@ public class Selection extends SpelNodeImpl {
|
||||
} finally {
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
if ((variant == FIRST || variant == LAST) && result.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
if (variant == LAST) {
|
||||
return new TypedValue(result.get(result.size() - 1),TypeDescriptor.valueOf(op.getTypeDescriptor().getElementType()));
|
||||
}
|
||||
}
|
||||
if ((variant == FIRST || variant == LAST) && result.size() == 0) {
|
||||
return new TypedValue(null,TypeDescriptor.NULL_TYPE_DESCRIPTOR);
|
||||
}
|
||||
if (variant == LAST) {
|
||||
Object lastValue = result.get(lastKey);
|
||||
Map resultMap = new HashMap();
|
||||
resultMap.put(lastKey,result.get(lastKey));
|
||||
return new TypedValue(resultMap,TypeDescriptor.valueOf(Map.class));
|
||||
}
|
||||
return new TypedValue(result,op.getTypeDescriptor());
|
||||
} else if (operand instanceof Collection) {
|
||||
|
||||
@@ -16,16 +16,14 @@
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
enum TypeCode {
|
||||
public enum TypeCode {
|
||||
|
||||
OBJECT(0, Object.class), BOOLEAN(1, Boolean.TYPE), BYTE(1, Byte.TYPE), CHAR(1, Character.TYPE), SHORT(2, Short.TYPE), INT(
|
||||
3, Integer.TYPE), LONG(4, Long.TYPE), FLOAT(5, Float.TYPE), DOUBLE(6, Double.TYPE);
|
||||
OBJECT(Object.class), BOOLEAN(Boolean.TYPE), BYTE(Byte.TYPE), CHAR(Character.TYPE), //
|
||||
SHORT(Short.TYPE), INT(Integer.TYPE), LONG(Long.TYPE), FLOAT(Float.TYPE), DOUBLE(Double.TYPE);
|
||||
|
||||
private int code;
|
||||
private Class<?> type;
|
||||
|
||||
TypeCode(int code, Class<?> type) {
|
||||
this.code = code;
|
||||
TypeCode(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@@ -33,72 +31,15 @@ enum TypeCode {
|
||||
return type;
|
||||
}
|
||||
|
||||
public static TypeCode forClass(Class<?> c) {
|
||||
TypeCode[] allValues = TypeCode.values();
|
||||
for (int i = 0; i < allValues.length; i++) {
|
||||
TypeCode typeCode = allValues[i];
|
||||
if (c == typeCode.getType()) {
|
||||
return typeCode;
|
||||
}
|
||||
}
|
||||
return OBJECT;
|
||||
}
|
||||
// public static TypeCode forClass(Class<?> c) {
|
||||
// TypeCode[] allValues = TypeCode.values();
|
||||
// for (int i = 0; i < allValues.length; i++) {
|
||||
// TypeCode typeCode = allValues[i];
|
||||
// if (c == typeCode.getType()) {
|
||||
// return typeCode;
|
||||
// }
|
||||
// }
|
||||
// return OBJECT;
|
||||
// }
|
||||
|
||||
/**
|
||||
* For a primitive name this will determine the typecode value - supports
|
||||
* int,byte,char,short,long,double,float,boolean
|
||||
*/
|
||||
public static TypeCode forName(String name) {
|
||||
if (name.equals("int"))
|
||||
return TypeCode.INT;
|
||||
else if (name.equals("boolean"))
|
||||
return TypeCode.BOOLEAN;
|
||||
else if (name.equals("char"))
|
||||
return TypeCode.CHAR;
|
||||
else if (name.equals("long"))
|
||||
return TypeCode.LONG;
|
||||
else if (name.equals("float"))
|
||||
return TypeCode.FLOAT;
|
||||
else if (name.equals("double"))
|
||||
return TypeCode.DOUBLE;
|
||||
else if (name.equals("short"))
|
||||
return TypeCode.SHORT;
|
||||
else if (name.equals("byte"))
|
||||
return TypeCode.BYTE;
|
||||
return TypeCode.OBJECT;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public Object coerce(TypeCode fromTypeCode, Object fromObject) {
|
||||
if (this == TypeCode.INT) {
|
||||
switch (fromTypeCode) {
|
||||
case BOOLEAN:
|
||||
return ((Boolean) fromObject).booleanValue() ? 1 : 0;
|
||||
}
|
||||
}
|
||||
//
|
||||
// return Integer.valueOf
|
||||
// } else if (this==TypeCode.BOOLEAN) {
|
||||
// return new Boolean(left).intValue();
|
||||
return null;
|
||||
}
|
||||
|
||||
public static TypeCode forValue(Number op1) {
|
||||
return forClass(op1.getClass());
|
||||
}
|
||||
|
||||
public boolean isDouble() {
|
||||
return this == DOUBLE;
|
||||
}
|
||||
|
||||
public boolean isFloat() {
|
||||
return this == FLOAT;
|
||||
}
|
||||
|
||||
public boolean isLong() {
|
||||
return this == LONG;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class TypeReference extends SpelNodeImpl {
|
||||
// TODO possible optimization here if we cache the discovered type reference, but can we do that?
|
||||
String typename = (String) getChild(0).getValueInternal(state).getValue();
|
||||
if (typename.indexOf(".") == -1 && Character.isLowerCase(typename.charAt(0))) {
|
||||
TypeCode tc = TypeCode.forName(typename);
|
||||
TypeCode tc = TypeCode.valueOf(typename.toUpperCase());
|
||||
if (tc != TypeCode.OBJECT) {
|
||||
// it is a primitive type
|
||||
return new TypedValue(tc.getType(),CLASS_TYPE_DESCRIPTOR);
|
||||
|
||||
@@ -20,7 +20,6 @@ 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;
|
||||
|
||||
/**
|
||||
* Represents a variable reference, eg. #someVar. Note this is different to a *local* variable like $someVar
|
||||
@@ -52,9 +51,7 @@ public class VariableReference extends SpelNodeImpl {
|
||||
return state.getRootContextObject();
|
||||
}
|
||||
TypedValue result = state.lookupVariable(this.name);
|
||||
if (result == null) {
|
||||
throw new SpelException(getCharPositionInLine(), SpelMessages.VARIABLE_NOT_FOUND, this.name);
|
||||
}
|
||||
// a null value will mean either the value was null or the variable was not found
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -64,7 +61,7 @@ public class VariableReference extends SpelNodeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
public String toStringAST() {
|
||||
return "#" + this.name;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,11 +70,9 @@ public class ReflectiveConstructorResolver implements ConstructorResolver {
|
||||
if (matchInfo != null) {
|
||||
if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.EXACT) {
|
||||
return new ReflectiveConstructorExecutor(ctor, null);
|
||||
}
|
||||
else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.CLOSE) {
|
||||
} else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.CLOSE) {
|
||||
closeMatch = ctor;
|
||||
}
|
||||
else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
} else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
argsToConvert = matchInfo.argsRequiringConversion;
|
||||
matchRequiringConversion = ctor;
|
||||
}
|
||||
@@ -82,11 +80,9 @@ public class ReflectiveConstructorResolver implements ConstructorResolver {
|
||||
}
|
||||
if (closeMatch != null) {
|
||||
return new ReflectiveConstructorExecutor(closeMatch, null);
|
||||
}
|
||||
else if (matchRequiringConversion != null) {
|
||||
} else if (matchRequiringConversion != null) {
|
||||
return new ReflectiveConstructorExecutor(matchRequiringConversion, argsToConvert);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
|
||||
Method method = findGetterForProperty(name, type, target instanceof Class);
|
||||
if (method != null) {
|
||||
this.readerCache.put(cacheKey, method);
|
||||
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(new MethodParameter(method,0)));
|
||||
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(new MethodParameter(method,-1)));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -85,8 +85,8 @@ public class StandardEvaluationContext implements EvaluationContext {
|
||||
this.variables.put(name, value);
|
||||
}
|
||||
|
||||
public void registerFunction(String name, Method m) {
|
||||
this.variables.put(name, m);
|
||||
public void registerFunction(String name, Method method) {
|
||||
this.variables.put(name, method);
|
||||
}
|
||||
|
||||
public Object lookupVariable(String name) {
|
||||
|
||||
@@ -76,6 +76,31 @@ public class ConstructorInvocationTests extends ExpressionTestCase {
|
||||
// evaluate("new String(new char[]{'h','e','l','l','o'})", "hello", String.class);
|
||||
}
|
||||
|
||||
public void testNonExistentType() {
|
||||
evaluateAndCheckError("new FooBar()",SpelMessages.PROBLEM_LOCATING_CONSTRUCTOR);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
@@ -61,21 +61,25 @@ public class EvaluationTests extends ExpressionTestCase {
|
||||
// evaluate("name in {null, \"Anonymous\"}", "false", Boolean.class);
|
||||
// }
|
||||
//
|
||||
// public void testRelOperatorsBetween01() {
|
||||
// evaluate("1 between {1, 5}", "true", Boolean.class);
|
||||
// }
|
||||
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
|
||||
}
|
||||
//
|
||||
// public void testRelOperatorsBetween02() {
|
||||
// evaluate("'efg' between {'abc', 'xyz'}", "true", Boolean.class);
|
||||
// }
|
||||
//
|
||||
// public void testRelOperatorsBetweenErrors01() {
|
||||
// evaluateAndCheckError("1 between T(String)", SpelMessages.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST, 12);
|
||||
// }
|
||||
public void testRelOperatorsBetweenErrors01() {
|
||||
evaluateAndCheckError("1 between T(String)", SpelMessages.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST, 12);
|
||||
}
|
||||
//
|
||||
// public void testRelOperatorsBetweenErrors02() {
|
||||
// evaluateAndCheckError("'abc' between {5,7}", SpelMessages.NOT_COMPARABLE, 6);
|
||||
// }
|
||||
public void testRelOperatorsBetweenErrors03() {
|
||||
evaluateAndCheckError("1 between listOfNumbersUpToTen", SpelMessages.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST, 10);
|
||||
}
|
||||
|
||||
public void testRelOperatorsIs01() {
|
||||
evaluate("'xyz' instanceof T(int)", "false", Boolean.class);
|
||||
@@ -343,18 +347,30 @@ public class EvaluationTests extends ExpressionTestCase {
|
||||
// evaluate("{1,2,3,4,5,6,7,8,9,10}.?{$index > 5 }", "[7, 8, 9, 10]", ArrayList.class);
|
||||
// }
|
||||
|
||||
public void testSelection03() {
|
||||
public void testSelection03() {
|
||||
evaluate("mapOfNumbersUpToTen.?{key>5}.size()", "5", Integer.class);
|
||||
}
|
||||
}
|
||||
|
||||
public void testSelection04() {
|
||||
evaluateAndCheckError("mapOfNumbersUpToTen.?{'hello'}.size()",SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);
|
||||
}
|
||||
|
||||
public void testSelectionFirst01() {
|
||||
evaluate("listOfNumbersUpToTen.^{#isEven(#this) == 'y'}", "2", Integer.class);
|
||||
}
|
||||
|
||||
|
||||
public void testSelectionFirst02() {
|
||||
evaluate("mapOfNumbersUpToTen.^{key>5}.size()", "1", Integer.class);
|
||||
}
|
||||
|
||||
public void testSelectionLast01() {
|
||||
evaluate("listOfNumbersUpToTen.${#isEven(#this) == 'y'}", "10", Integer.class);
|
||||
}
|
||||
|
||||
public void testSelectionLast02() {
|
||||
evaluate("mapOfNumbersUpToTen.${key>5}.size()", "1", Integer.class);
|
||||
}
|
||||
|
||||
public void testSelectionAST() throws Exception {
|
||||
SpelExpression expr = (SpelExpression)parser.parseExpression("'abc'.^{true}");
|
||||
assertEquals("'abc'.^{true}",expr.toStringAST());
|
||||
@@ -411,6 +427,10 @@ public class EvaluationTests extends ExpressionTestCase {
|
||||
public void testIndexer03() {
|
||||
evaluate("'christian'[8]", "n", String.class);
|
||||
}
|
||||
|
||||
public void testIndexerError() {
|
||||
evaluateAndCheckError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",SpelMessages.CANNOT_INDEX_INTO_NULL_VALUE);
|
||||
}
|
||||
|
||||
// Bean references
|
||||
// public void testReferences01() {
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
///CLOVER:OFF
|
||||
/**
|
||||
* Common superclass for expression tests.
|
||||
*
|
||||
|
||||
@@ -37,4 +37,9 @@ public class HelperTests extends ExpressionTestCase {
|
||||
assertEquals("goo(java.lang.String,int[])",FormatHelper.formatMethodForMessage("goo", String.class,new int[1].getClass()));
|
||||
assertEquals("boo()",FormatHelper.formatMethodForMessage("boo"));
|
||||
}
|
||||
|
||||
// public void testReflectionHelper01() {
|
||||
// ReflectionHelper.convertArguments(parameterTypes, isVarargs, converter, arguments)
|
||||
//// ReflectionHelper.setupArgumentsForVarargsInvocation(;, args)
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -85,5 +85,9 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
evaluate("aVarargsMethod2(2,'a',3.0d)", 4, Integer.class);
|
||||
// evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", 11, Integer.class);
|
||||
}
|
||||
|
||||
public void testInvocationOnNullContextObject() {
|
||||
evaluateAndCheckError("null.toString()",SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.OperatorOverloader;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void testSimpleOperations() throws Exception {
|
||||
// no built in support for this:
|
||||
evaluateAndCheckError("'abc'+true",SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
|
||||
StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
|
||||
eContext.setOperatorOverloader(new StringAndBooleanAddition());
|
||||
|
||||
SpelExpression expr = (SpelExpression)parser.parseExpression("'abc'+true");
|
||||
assertEquals("abctrue",expr.getValue(eContext));
|
||||
|
||||
expr = (SpelExpression)parser.parseExpression("'abc'-true");
|
||||
assertEquals("abc",expr.getValue(eContext));
|
||||
}
|
||||
}
|
||||
@@ -233,6 +233,9 @@ public class OperatorTests extends ExpressionTestCase {
|
||||
|
||||
node = getOperatorNode((SpelExpression)parser.parseExpression("3>=4"));
|
||||
assertEquals(">=",node.getOperatorName());
|
||||
|
||||
node = getOperatorNode((SpelExpression)parser.parseExpression("3 between 4"));
|
||||
assertEquals("between",node.getOperatorName());
|
||||
}
|
||||
|
||||
public void testOperatorOverloading() {
|
||||
|
||||
@@ -35,6 +35,8 @@ public class PerformanceTests extends TestCase {
|
||||
private static SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser();
|
||||
private static EvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
public void testPerformanceOfPropertyAccess() throws Exception {
|
||||
long starttime = 0;
|
||||
long endtime = 0;
|
||||
@@ -58,7 +60,9 @@ public class PerformanceTests extends TestCase {
|
||||
}
|
||||
endtime = System.currentTimeMillis();
|
||||
long freshParseTime = endtime - starttime;
|
||||
System.out.println(freshParseTime);
|
||||
if (DEBUG) {
|
||||
System.out.println("PropertyAccess: Time for parsing and evaluation x 10000: "+freshParseTime+"ms");
|
||||
}
|
||||
|
||||
Expression expr = parser.parseExpression("placeOfBirth.city");
|
||||
if (expr == null) {
|
||||
@@ -70,7 +74,9 @@ public class PerformanceTests extends TestCase {
|
||||
}
|
||||
endtime = System.currentTimeMillis();
|
||||
long reuseTime = endtime - starttime;
|
||||
System.out.println(reuseTime);
|
||||
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");
|
||||
@@ -101,7 +107,9 @@ public class PerformanceTests extends TestCase {
|
||||
}
|
||||
endtime = System.currentTimeMillis();
|
||||
long freshParseTime = endtime - starttime;
|
||||
System.out.println(freshParseTime);
|
||||
if (DEBUG) {
|
||||
System.out.println("MethodExpression: Time for parsing and evaluation x 10000: "+freshParseTime+"ms");
|
||||
}
|
||||
|
||||
Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()");
|
||||
if (expr == null) {
|
||||
@@ -113,7 +121,10 @@ public class PerformanceTests extends TestCase {
|
||||
}
|
||||
endtime = System.currentTimeMillis();
|
||||
long reuseTime = endtime - starttime;
|
||||
System.out.println(reuseTime);
|
||||
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");
|
||||
|
||||
@@ -41,6 +41,10 @@ public class PropertyAccessTests extends ExpressionTestCase {
|
||||
evaluate("placeOfBirth.city", "SmilJan", String.class);
|
||||
}
|
||||
|
||||
public void testSimpleAccess03() {
|
||||
evaluate("stringArrayOfThreeItems.length", "3", Integer.class);
|
||||
}
|
||||
|
||||
public void testNonExistentPropertiesAndMethods() {
|
||||
// madeup does not exist as a property
|
||||
evaluateAndCheckError("madeup", SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, 0);
|
||||
@@ -79,7 +83,7 @@ public class PropertyAccessTests extends ExpressionTestCase {
|
||||
} 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());
|
||||
// System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
|
||||
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
|
||||
*
|
||||
|
||||
@@ -47,7 +47,11 @@ public class SetValueTests extends ExpressionTestCase {
|
||||
public void testSetArrayElementValue() {
|
||||
setValue("inventions[0]", "Just the telephone");
|
||||
}
|
||||
|
||||
|
||||
public void testSetElementOfNull() {
|
||||
setValueExpectError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",SpelMessages.CANNOT_INDEX_INTO_NULL_VALUE);
|
||||
}
|
||||
|
||||
public void testSetArrayElementValueAllPrimitiveTypes() {
|
||||
setValue("arrayContainer.ints[1]", 3);
|
||||
setValue("arrayContainer.floats[1]", 3.0f);
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* 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 org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
|
||||
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>();
|
||||
IEEE() {
|
||||
officers.put("president",pupin);
|
||||
List linv = new ArrayList();
|
||||
linv.add(tesla);
|
||||
officers.put("advisors",linv);
|
||||
Members2.add(tesla);
|
||||
Members2.add(pupin);
|
||||
}
|
||||
|
||||
public boolean isMember(String name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String n) { this.name = n; }
|
||||
}
|
||||
|
||||
public void testMethodInvocation() {
|
||||
evaluate("'Hello World'.concat('!')","Hello World!",String.class);
|
||||
}
|
||||
|
||||
public void testBeanPropertyAccess() {
|
||||
evaluate("new String('Hello World'.bytes)","Hello World",String.class);
|
||||
}
|
||||
|
||||
public void testArrayLengthAccess() {
|
||||
evaluate("'Hello World'.bytes.length",11,Integer.class);
|
||||
}
|
||||
|
||||
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 SpelAntlrExpressionParser();
|
||||
Expression exp = parser.parseExpression("name");
|
||||
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
context.setRootObject(tesla);
|
||||
|
||||
String name = (String) exp.getValue(context);
|
||||
assertEquals("Nikola Tesla",name);
|
||||
}
|
||||
|
||||
public void testEqualityCheck() throws Exception {
|
||||
|
||||
ExpressionParser parser = new SpelAntlrExpressionParser();
|
||||
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
context.setRootObject(tesla);
|
||||
|
||||
Expression exp = parser.parseExpression("name == 'Nikola Tesla'");
|
||||
boolean isEqual = exp.getValue(context, Boolean.class); // evaluates to true
|
||||
assertTrue(isEqual);
|
||||
}
|
||||
|
||||
// Section 7.4.1
|
||||
|
||||
public void testXMLBasedConfig() {
|
||||
evaluate("(T(java.lang.Math).random() * 100.0 )>0",true,Boolean.class);
|
||||
}
|
||||
|
||||
// Section 7.5
|
||||
public void testLiterals() throws Exception {
|
||||
ExpressionParser parser = new SpelAntlrExpressionParser();
|
||||
|
||||
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); // evals to "Hello World"
|
||||
assertEquals("Hello World",helloWorld);
|
||||
|
||||
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
|
||||
assertEquals(6.0221415E+23,avogadrosNumber);
|
||||
|
||||
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); // evals to 2147483647
|
||||
assertEquals(Integer.MAX_VALUE,maxValue);
|
||||
|
||||
boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
|
||||
assertTrue(trueValue);
|
||||
|
||||
Object nullValue = parser.parseExpression("null").getValue();
|
||||
assertNull(nullValue);
|
||||
}
|
||||
|
||||
public void testPropertyAccess() throws Exception {
|
||||
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
|
||||
int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); // 1856
|
||||
assertEquals(1856,year);
|
||||
|
||||
String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);
|
||||
assertEquals("SmilJan",city);
|
||||
}
|
||||
|
||||
public void testPropertyNavigation() throws Exception {
|
||||
ExpressionParser parser = new SpelAntlrExpressionParser();
|
||||
|
||||
// Inventions Array
|
||||
StandardEvaluationContext teslaContext = TestScenarioCreator.getTestEvaluationContext();
|
||||
// teslaContext.setRootObject(tesla);
|
||||
|
||||
// evaluates to "Induction motor"
|
||||
String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class);
|
||||
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);
|
||||
assertEquals("Nikola Tesla",name);
|
||||
|
||||
// List and Array navigation
|
||||
// evaluates to "Wireless communication"
|
||||
invention = parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext, String.class);
|
||||
assertEquals("Wireless communication",invention);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
assertEquals("Nikola Tesla",i.getName());
|
||||
|
||||
parser.parseExpression("officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext, "Croatia");
|
||||
|
||||
}
|
||||
|
||||
// 7.5.3
|
||||
|
||||
public void testMethodInvocation2() throws Exception {
|
||||
// string literal, evaluates to "bc"
|
||||
String c = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);
|
||||
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);
|
||||
assertTrue(isMember);
|
||||
}
|
||||
|
||||
// 7.5.4.1
|
||||
|
||||
public void testRelationalOperators() throws Exception {
|
||||
boolean result = parser.parseExpression("2 == 2").getValue(Boolean.class);
|
||||
assertTrue(result);
|
||||
// evaluates to false
|
||||
result = parser.parseExpression("2 < -5.0").getValue(Boolean.class);
|
||||
assertFalse(result);
|
||||
|
||||
// evaluates to true
|
||||
result = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
public void testOtherOperators() throws Exception {
|
||||
// evaluates to false
|
||||
boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class);
|
||||
assertFalse(falseValue);
|
||||
|
||||
// evaluates to true
|
||||
boolean trueValue = parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
|
||||
assertTrue(trueValue);
|
||||
|
||||
//evaluates to false
|
||||
falseValue = parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
|
||||
assertFalse(falseValue);
|
||||
}
|
||||
|
||||
// 7.5.4.2
|
||||
|
||||
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);
|
||||
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);
|
||||
assertTrue(trueValue);
|
||||
|
||||
// evaluates to true
|
||||
expression = "isMember('Nikola Tesla') or isMember('Albert Einstien')";
|
||||
trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
|
||||
assertTrue(trueValue);
|
||||
|
||||
// -- NOT --
|
||||
|
||||
// evaluates to false
|
||||
falseValue = parser.parseExpression("!true").getValue(Boolean.class);
|
||||
assertFalse(falseValue);
|
||||
|
||||
|
||||
// -- AND and NOT --
|
||||
expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
|
||||
falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
|
||||
assertFalse(falseValue);
|
||||
}
|
||||
|
||||
// 7.5.4.3
|
||||
|
||||
public void testNumericalOperators() throws Exception {
|
||||
// Addition
|
||||
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
|
||||
assertEquals(2,two);
|
||||
|
||||
String testString = parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string'
|
||||
assertEquals("test string",testString);
|
||||
|
||||
// Subtraction
|
||||
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4
|
||||
assertEquals(4,four);
|
||||
|
||||
double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000
|
||||
assertEquals(-9000.0d,d);
|
||||
|
||||
// Multiplication
|
||||
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6
|
||||
assertEquals(6,six);
|
||||
|
||||
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0
|
||||
assertEquals(24.0d,twentyFour);
|
||||
|
||||
// Division
|
||||
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2
|
||||
assertEquals(-2,minusTwo);
|
||||
|
||||
double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0
|
||||
assertEquals(1.0d,one);
|
||||
|
||||
// Modulus
|
||||
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3
|
||||
assertEquals(3,three);
|
||||
|
||||
int oneInt = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1
|
||||
assertEquals(1,oneInt);
|
||||
|
||||
// Operator precedence
|
||||
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
|
||||
assertEquals(-21,minusTwentyOne);
|
||||
}
|
||||
|
||||
// 7.5.5
|
||||
|
||||
public void testAssignment() throws Exception {
|
||||
Inventor inventor = new Inventor();
|
||||
StandardEvaluationContext inventorContext = new StandardEvaluationContext();
|
||||
inventorContext.setRootObject(inventor);
|
||||
|
||||
parser.parseExpression("foo").setValue(inventorContext, "Alexander Seovic2");
|
||||
|
||||
assertEquals("Alexander Seovic2",parser.parseExpression("foo").getValue(inventorContext,String.class));
|
||||
// alternatively
|
||||
|
||||
String aleks = parser.parseExpression("foo = 'Alexandar Seovic'").getValue(inventorContext, String.class);
|
||||
assertEquals("Alexandar Seovic",parser.parseExpression("foo").getValue(inventorContext,String.class));
|
||||
assertEquals("Alexandar Seovic",aleks);
|
||||
}
|
||||
|
||||
// 7.5.6
|
||||
|
||||
public void testTypes() throws Exception {
|
||||
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
|
||||
assertEquals(Date.class,dateClass);
|
||||
boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class);
|
||||
assertTrue(trueValue);
|
||||
}
|
||||
|
||||
// 7.5.7
|
||||
|
||||
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);
|
||||
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
|
||||
|
||||
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);
|
||||
|
||||
assertEquals("Mike Tesla",tesla.getFoo());
|
||||
}
|
||||
|
||||
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 SpelAntlrExpressionParser();
|
||||
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);
|
||||
assertEquals("[11, 13, 17]",primesGreaterThanTen.toString());
|
||||
}
|
||||
|
||||
// 7.5.9
|
||||
|
||||
public void testFunctions() throws Exception {
|
||||
ExpressionParser parser = new SpelAntlrExpressionParser();
|
||||
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);
|
||||
assertEquals("dlrow olleh",helloWorldReversed);
|
||||
}
|
||||
|
||||
// 7.5.10
|
||||
|
||||
public void testTernary() throws Exception {
|
||||
String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);
|
||||
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);
|
||||
assertEquals("Nikola Tesla is a member of the IEEE Society",queryResultString);
|
||||
// queryResultString = "Nikola Tesla is a member of the IEEE Society"
|
||||
}
|
||||
|
||||
// 7.5.11
|
||||
|
||||
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);
|
||||
assertEquals(1,list.size());
|
||||
assertEquals("Nikola Tesla",list.get(0).getName());
|
||||
}
|
||||
|
||||
// 7.5.12
|
||||
|
||||
public void testTemplating() throws Exception {
|
||||
String randomPhrase =
|
||||
parser.parseExpression("random number is ${T(java.lang.Math).random()}", new TemplatedParserContext()).getValue(String.class);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ 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>
|
||||
|
||||
@@ -10,7 +10,8 @@ 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;
|
||||
@@ -20,6 +21,18 @@ public class Fruit {
|
||||
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.startsWith("o") ? "n " : " ") + colorName + " " + name;
|
||||
|
||||
@@ -12,7 +12,7 @@ public class Inventor {
|
||||
private String name;
|
||||
public String publicName;
|
||||
private PlaceOfBirth placeOfBirth;
|
||||
Date birthdate;
|
||||
private Date birthdate;
|
||||
private int sinNumber;
|
||||
private String nationality;
|
||||
private String[] inventions;
|
||||
@@ -29,6 +29,9 @@ public class Inventor {
|
||||
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 Inventor(String name, Date birthdate, String nationality) {
|
||||
this.name = name;
|
||||
@@ -43,6 +46,8 @@ public class Inventor {
|
||||
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);
|
||||
@@ -148,4 +153,11 @@ public class Inventor {
|
||||
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; }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package org.springframework.expression.spel.testresources;
|
||||
public class PlaceOfBirth {
|
||||
private String city;
|
||||
|
||||
public String Country;
|
||||
|
||||
@Override
|
||||
public String toString() {return "PlaceOfBirth("+city+")";}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user