objects flowing around in expression evaluation are now TypedValue's - these carry generics info, used for conversions.

This commit is contained in:
Andy Clement 2009-04-03 23:39:14 +00:00
parent f6c1144e8d
commit 4c5854d017
66 changed files with 749 additions and 382 deletions

View File

@ -17,15 +17,18 @@
package org.springframework.context.expression;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
/**
* EL property accessor that knows how to traverse the beans and contextual objects
* of a Spring {@link org.springframework.beans.factory.config.BeanExpressionContext}.
*
* @author Juergen Hoeller
* @author Andy Clement
* @since 3.0
*/
public class BeanExpressionContextAccessor implements PropertyAccessor {
@ -34,8 +37,8 @@ public class BeanExpressionContextAccessor implements PropertyAccessor {
return (((BeanExpressionContext) target).containsObject(name));
}
public Object read(EvaluationContext context, Object target, String name) throws AccessException {
return ((BeanExpressionContext) target).getObject(name);
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((BeanExpressionContext) target).getObject(name));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {

View File

@ -17,15 +17,18 @@
package org.springframework.context.expression;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
/**
* EL property accessor that knows how to traverse the beans of a
* Spring {@link org.springframework.beans.factory.BeanFactory}.
*
* @author Juergen Hoeller
* @author Andy Clement
* @since 3.0
*/
public class BeanFactoryAccessor implements PropertyAccessor {
@ -34,8 +37,8 @@ public class BeanFactoryAccessor implements PropertyAccessor {
return (((BeanFactory) target).containsBean(name));
}
public Object read(EvaluationContext context, Object target, String name) throws AccessException {
return ((BeanFactory) target).getBean(name);
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((BeanFactory) target).getBean(name));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {

View File

@ -21,12 +21,15 @@ import java.util.Map;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ast.CommonTypeDescriptors;
/**
* EL property accessor that knows how to traverse the keys
* of a standard {@link java.util.Map}.
*
* @author Juergen Hoeller
* @author Andy Clement
* @since 3.0
*/
public class MapAccessor implements PropertyAccessor {
@ -35,8 +38,8 @@ public class MapAccessor implements PropertyAccessor {
return (((Map) target).containsKey(name));
}
public Object read(EvaluationContext context, Object target, String name) throws AccessException {
return ((Map) target).get(name);
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((Map) target).get(name),CommonTypeDescriptors.OBJECT_TYPE_DESCRIPTOR);
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {

View File

@ -16,6 +16,7 @@
package org.springframework.expression;
// TODO Is the resolver/executor model too pervasive in this package?
/**
* Executors are built by resolvers and can be cached by the infrastructure to repeat an operation quickly without going
@ -39,6 +40,6 @@ public interface ConstructorExecutor {
* @return the new object
* @throws AccessException if there is a problem executing the command or the CommandExecutor is no longer valid
*/
Object execute(EvaluationContext context, Object... arguments) throws AccessException;
TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException;
}

View File

@ -35,7 +35,7 @@ public interface EvaluationContext {
/**
* @return the root context object against which unqualified properties/methods/etc should be resolved
*/
Object getRootObject();
TypedValue getRootObject();
/**
* Set a named variable within this execution context to a specified value.

View File

@ -39,6 +39,6 @@ public interface MethodExecutor {
* @return the value returned from execution
* @throws AccessException if there is a problem executing the command or the MethodExecutor is no longer valid
*/
Object execute(EvaluationContext context, Object target, Object... arguments) throws AccessException;
TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException;
}

View File

@ -58,10 +58,10 @@ public interface PropertyAccessor {
* @param context the evaluation context in which the access is being attempted
* @param target the target object upon which the property is being accessed
* @param name the name of the property being accessed
* @return Object the value of the property
* @return a TypedValue object wrapping the property value read and a type descriptor for it
* @throws AccessException if there is any problem accessing the property value
*/
Object read(EvaluationContext context, Object target, String name) throws AccessException;
TypedValue read(EvaluationContext context, Object target, String name) throws AccessException;
/**
* Called to determine if a resolver instance is able to write to a specified property on a specified target object.

View File

@ -0,0 +1,61 @@
/*
* 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;
import org.springframework.core.convert.TypeDescriptor;
/**
* Encapsulates an object and a type descriptor that describes it.
* The type descriptor can hold generic information that would
* not be accessible through a simple getClass() call on the object.
*
* @author Andy Clement
* @since 3.0
*/
public class TypedValue {
private Object value;
private TypeDescriptor typeDescriptor;
/**
* Create a TypedValue for a simple object. The type descriptor is inferred
* from the object, so no generic information is preserved.
* @param value the object value
*/
public TypedValue(Object value) {
this.value = value;
this.typeDescriptor = TypeDescriptor.forObject(value);
}
/**
* Create a TypedValue for a particular value with a particular type descriptor.
* @param value the object value
* @param typeDescriptor a type descriptor describing the type of the value
*/
public TypedValue(Object value, TypeDescriptor typeDescriptor) {
this.value = value;
this.typeDescriptor = typeDescriptor;
}
public Object getValue() {
return this.value;
}
public TypeDescriptor getTypeDescriptor() {
return this.typeDescriptor;
}
}

View File

@ -27,6 +27,7 @@ import org.springframework.expression.Operation;
import org.springframework.expression.OperatorOverloader;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.TypedValue;
/**
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to it are not seen by other
@ -45,7 +46,7 @@ public class ExpressionState {
private final Stack<VariableScope> variableScopes = new Stack<VariableScope>();
private final Stack<Object> contextObjects = new Stack<Object>();
private final Stack<TypedValue> contextObjects = new Stack<TypedValue>();
public ExpressionState() {
@ -65,14 +66,18 @@ public class ExpressionState {
/**
* The active context object is what unqualified references to properties/etc are resolved against.
*/
public Object getActiveContextObject() {
public TypedValue getActiveContextObject() {
if (this.contextObjects.isEmpty()) {
return this.relatedContext.getRootObject();
TypedValue rootObject = this.relatedContext.getRootObject();
if (rootObject == null) {
return new TypedValue(rootObject,TypeDescriptor.NULL_TYPE_DESCRIPTOR);
}
return rootObject;
}
return this.contextObjects.peek();
}
public void pushActiveContextObject(Object obj) {
public void pushActiveContextObject(TypedValue obj) {
this.contextObjects.push(obj);
}
@ -80,7 +85,7 @@ public class ExpressionState {
this.contextObjects.pop();
}
public Object getRootContextObject() {
public TypedValue getRootContextObject() {
return this.relatedContext.getRootObject();
}
@ -88,8 +93,9 @@ public class ExpressionState {
this.relatedContext.setVariable(name, value);
}
public Object lookupVariable(String name) {
return this.relatedContext.lookupVariable(name);
public TypedValue lookupVariable(String name) {
Object value = this.relatedContext.lookupVariable(name);
return new TypedValue(value,TypeDescriptor.valueOf((value==null?null:value.getClass())));
}
public TypeComparator getTypeComparator() {
@ -103,11 +109,15 @@ public class ExpressionState {
public <T> T convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
return this.relatedContext.getTypeConverter().convertValue(value, targetTypeDescriptor);
}
public <T> T convertValue(Object value, Class<T> targetType) throws EvaluationException {
return this.relatedContext.getTypeConverter().convertValue(value, targetType);
public <T> T convertValue(TypedValue value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
return this.relatedContext.getTypeConverter().convertValue(value.getValue(), targetTypeDescriptor);
}
// public <T> T convertValue(TypedValue value, Class<T> targetType) throws EvaluationException {
// return this.relatedContext.getTypeConverter().convertValue(value, targetType);
// }
/**
* A new scope is entered when a function is invoked
*/
@ -137,10 +147,11 @@ public class ExpressionState {
return null;
}
public Object operate(Operation op, Object left, Object right) throws EvaluationException {
public TypedValue operate(Operation op, Object left, Object right) throws EvaluationException {
OperatorOverloader overloader = this.relatedContext.getOperatorOverloader();
if (overloader.overridesOperation(op, left, right)) {
return overloader.operate(op, left, right);
Object returnValue = overloader.operate(op, left, right);
return new TypedValue(returnValue,TypeDescriptor.forObject(returnValue));
}
else {
throw new SpelException(SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, left, right);

View File

@ -18,8 +18,9 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
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.
@ -36,9 +37,9 @@ public class Assign extends SpelNodeImpl {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object newValue = getChild(1).getValueInternal(state);
getChild(0).setValue(state, newValue);
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue newValue = getChild(1).getValueInternal(state);
getChild(0).setValue(state, newValue.getValue());
return newValue;
}

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Represents the literal values TRUE and FALSE.
@ -26,15 +27,15 @@ import org.antlr.runtime.Token;
*/
public class BooleanLiteral extends Literal {
private final Boolean value;
private final BooleanTypedValue value;
public BooleanLiteral(Token payload, boolean value) {
super(payload);
this.value = value;
this.value = BooleanTypedValue.forValue(value);
}
@Override
public Boolean getLiteralValue() {
public BooleanTypedValue getLiteralValue() {
return this.value;
}

View File

@ -0,0 +1,38 @@
/*
* 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.springframework.core.convert.TypeDescriptor;
/**
* @author Andy Clement
* @since 3.0
*/
public interface CommonTypeDescriptors {
// TODO push into TypeDescriptor?
static TypeDescriptor BOOLEAN_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Boolean.class);
static TypeDescriptor INTEGER_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Integer.class);
static TypeDescriptor CHARACTER_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Character.class);
static TypeDescriptor LONG_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Long.class);
static TypeDescriptor SHORT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Short.class);
static TypeDescriptor BYTE_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Byte.class);
static TypeDescriptor FLOAT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Float.class);
static TypeDescriptor DOUBLE_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Double.class);
static TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
static TypeDescriptor CLASS_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Class.class);
static TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);
}

View File

@ -18,8 +18,9 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
/**
* Represents a DOT separated expression sequence, such as 'property1.property2.methodOne()'
@ -40,8 +41,8 @@ public class CompoundExpression extends SpelNodeImpl {
* @return the final value from the last piece of the compound expression
*/
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object result = null;
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue result = null;
SpelNodeImpl nextNode = null;
try {
nextNode = getChild(0);
@ -69,7 +70,7 @@ public class CompoundExpression extends SpelNodeImpl {
getChild(0).setValue(state, value);
return;
}
Object ctx = getChild(0).getValueInternal(state);
TypedValue ctx = getChild(0).getValueInternal(state);
for (int i = 1; i < getChildCount() - 1; i++) {
try {
state.pushActiveContextObject(ctx);
@ -91,7 +92,7 @@ public class CompoundExpression extends SpelNodeImpl {
if (getChildCount() == 1) {
return getChild(0).isWritable(state);
}
Object ctx = getChild(0).getValueInternal(state);
TypedValue ctx = getChild(0).getValueInternal(state);
for (int i = 1; i < getChildCount() - 1; i++) {
try {
state.pushActiveContextObject(ctx);

View File

@ -20,12 +20,13 @@ 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;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
@ -67,7 +68,7 @@ public class ConstructorReference extends SpelNodeImpl {
* Implements getValue() - delegating to the code for building an array or a simple type.
*/
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
if (this.isArrayConstructor) {
return createArray(state);
}
@ -123,14 +124,14 @@ public class ConstructorReference extends SpelNodeImpl {
* @return the new array
* @throws EvaluationException if there is a problem creating the array
*/
private Object createArray(ExpressionState state) throws EvaluationException {
Object intendedArrayType = getChild(0).getValueInternal(state);
if (!(intendedArrayType instanceof String)) {
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;
String type = (String) intendedArrayType.getValue();
Class<?> componentType = null;
TypeCode arrayTypeCode = TypeCode.forName(type);
if (arrayTypeCode == TypeCode.OBJECT) {
@ -140,6 +141,7 @@ public class ConstructorReference extends SpelNodeImpl {
}
Object newArray = null;
TypeDescriptor newArrayTypeDescriptor = null;
if (getChild(1).getChildCount() == 0) { // are the array ranks defined?
if (getChildCount() < 3) {
@ -149,13 +151,14 @@ public class ConstructorReference extends SpelNodeImpl {
// 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.class);
int arraySize = state.convertValue(o, INTEGER_TYPE_DESCRIPTOR);
if (getChildCount() == 3) {
// Check initializer length matches array size length
int initializerLength = getChild(2).getChildCount();
@ -165,14 +168,16 @@ public class ConstructorReference extends SpelNodeImpl {
}
}
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.class);
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
}
}
@ -195,47 +200,47 @@ public class ConstructorReference extends SpelNodeImpl {
} 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.class);
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.class);
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.class);
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.class);
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.class);
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.class);
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.class);
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.class);
newByteArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), BYTE_TYPE_DESCRIPTOR);
}
}
}
return newArray;
return new TypedValue(newArray, newArrayTypeDescriptor);
}
/**
@ -244,13 +249,13 @@ public class ConstructorReference extends SpelNodeImpl {
* @return the new object
* @throws EvaluationException if there is a problem creating the object
*/
private Object createNewInstance(ExpressionState state) throws EvaluationException {
private TypedValue createNewInstance(ExpressionState state) throws EvaluationException {
Object[] arguments = new Object[getChildCount() - 1];
Class<?>[] argumentTypes = new Class[getChildCount() - 1];
for (int i = 0; i < arguments.length; i++) {
Object childValue = getChild(i + 1).getValueInternal(state);
arguments[i] = childValue;
argumentTypes[i] = childValue.getClass();
TypedValue childValue = getChild(i + 1).getValueInternal(state);
arguments[i] = childValue.getValue();
argumentTypes[i] = childValue.getValue().getClass();
}
ConstructorExecutor executorToUse = this.cachedExecutor;
@ -266,7 +271,7 @@ public class ConstructorReference extends SpelNodeImpl {
}
// either there was no accessor or it no longer exists
String typename = (String) getChild(0).getValueInternal(state);
String typename = (String) getChild(0).getValueInternal(state).getValue();
executorToUse = findExecutorForConstructor(typename, argumentTypes, state);
try {
return executorToUse.execute(state.getEvaluationContext(), arguments);

View File

@ -17,8 +17,9 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
/**
* This is used for preserving positional information from the input expression.
@ -39,7 +40,7 @@ public class Dot extends SpelNodeImpl {
}
@Override
public Object getValueInternal(ExpressionState state) throws SpelException {
public TypedValue getValueInternal(ExpressionState state) throws SpelException {
// This makes Dot a do-nothing operation, but this is not free in terms of computation
return state.getActiveContextObject();
}

View File

@ -17,8 +17,9 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
/**
* @author Andy Clement
@ -31,7 +32,7 @@ public class EmptySpelNode extends SpelNodeImpl {
}
@Override
public Object getValueInternal(ExpressionState state) throws SpelException {
public TypedValue getValueInternal(ExpressionState state) throws SpelException {
throw new RuntimeException("?");
}

View File

@ -21,9 +21,11 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.antlr.runtime.Token;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
@ -53,18 +55,18 @@ public class FunctionReference extends SpelNodeImpl {
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object o = state.lookupVariable(name);
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue o = state.lookupVariable(name);
if (o == null) {
throw new SpelException(SpelMessages.FUNCTION_NOT_DEFINED, name);
}
// Two possibilities: a lambda function or a Java static method registered as a function
if (!(o instanceof Method)) {
if (!(o.getValue() instanceof Method)) {
throw new SpelException(SpelMessages.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, name, o.getClass());
}
return executeFunctionJLRMethod(state, (Method) o);
return executeFunctionJLRMethod(state, (Method) o.getValue());
}
/**
@ -75,7 +77,7 @@ public class FunctionReference extends SpelNodeImpl {
* @return the return value of the invoked Java method
* @throws EvaluationException if there is any problem invoking the method
*/
private Object executeFunctionJLRMethod(ExpressionState state, Method m) throws EvaluationException {
private TypedValue executeFunctionJLRMethod(ExpressionState state, Method m) throws EvaluationException {
Object[] functionArgs = getArguments(state);
if (!m.isVarArgs() && m.getParameterTypes().length != functionArgs.length) {
@ -99,7 +101,7 @@ public class FunctionReference extends SpelNodeImpl {
}
try {
return m.invoke(m.getClass(), functionArgs);
return new TypedValue(m.invoke(m.getClass(), functionArgs),new TypeDescriptor(new MethodParameter(m,-1)));
} catch (IllegalArgumentException e) {
throw new SpelException(getCharPositionInLine(), e, SpelMessages.EXCEPTION_DURING_FUNCTION_CALL, name, e
.getMessage());
@ -139,7 +141,7 @@ public class FunctionReference extends SpelNodeImpl {
// Compute arguments to the function
Object[] arguments = new Object[getChildCount()];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = getChild(i).getValueInternal(state);
arguments[i] = getChild(i).getValueInternal(state).getValue();
}
return arguments;
}

View File

@ -17,9 +17,8 @@
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;
/**
* @author Andy Clement
@ -27,20 +26,20 @@ import org.springframework.expression.spel.SpelException;
*/
public class Identifier extends SpelNodeImpl {
private final String id;
private final TypedValue id;
public Identifier(Token payload) {
super(payload);
this.id = payload.getText();
this.id = new TypedValue(payload.getText(), STRING_TYPE_DESCRIPTOR);
}
@Override
public String toStringAST() {
return this.id;
return (String)this.id.getValue();
}
@Override
public String getValueInternal(ExpressionState state) {
public TypedValue getValueInternal(ExpressionState state) {
return this.id;
}

View File

@ -23,6 +23,7 @@ import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
@ -43,53 +44,44 @@ public class Indexer extends SpelNodeImpl {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object ctx = state.getActiveContextObject();
Object index = getChild(0).getValueInternal(state);
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue context = state.getActiveContextObject();
Object targetObject = context.getValue();
TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor();
TypedValue indexValue = getChild(0).getValueInternal(state);
Object index = indexValue.getValue();
// Indexing into a Map
if (ctx instanceof Map) {
return ((Map<?, ?>) ctx).get(index);
if (targetObject instanceof Map) {
Object possiblyConvertedKey = state.convertValue(indexValue,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapKeyType()));
Object o = ((Map<?, ?>) targetObject).get(possiblyConvertedKey);
return new TypedValue(o,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapValueType()));
}
int idx = state.convertValue(index, INTEGER_TYPE_DESCRIPTOR);
if (ctx.getClass().isArray()) {
return accessArrayElement(ctx, idx);
} else if (ctx instanceof Collection) {
Collection<?> c = (Collection<?>) ctx;
if (targetObjectTypeDescriptor.isArray()) {
return new TypedValue(accessArrayElement(targetObject, idx),TypeDescriptor.valueOf(targetObjectTypeDescriptor.getElementType()));
} else if (targetObjectTypeDescriptor.isCollection()) {
Collection<?> c = (Collection<?>) targetObject;
if (idx >= c.size()) {
throw new SpelException(SpelMessages.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
}
int pos = 0;
for (Object o : c) {
if (pos == idx) {
return o;
return new TypedValue(o,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getElementType()));
}
pos++;
}
// } else if (ctx instanceof Map) {
// Map<?,?> c = (Map<?,?>) ctx;
// // This code would allow a key/value pair to be pulled out by index from a map
// if (idx >= c.size()) {
// throw new ELException(ELMessages.COLLECTION_INDEX_OUT_OF_BOUNDS,c.size(),idx);
// }
// Set<?> keys = c.keySet();
// int pos = 0;
// for (Object k : keys) {
// if (pos==idx) {
// return new KeyValuePair(k,c.get(k));
// }
// pos++;
// }
} else if (ctx instanceof String) {
String ctxString = (String) ctx;
} else if (targetObject instanceof String) {
String ctxString = (String) targetObject;
if (idx >= ctxString.length()) {
throw new SpelException(SpelMessages.STRING_INDEX_OUT_OF_BOUNDS, ctxString.length(), idx);
}
return String.valueOf(ctxString.charAt(idx));
return new TypedValue(String.valueOf(ctxString.charAt(idx)),STRING_TYPE_DESCRIPTOR);
}
throw new SpelException(SpelMessages.INDEXING_NOT_SUPPORTED_FOR_TYPE, ctx.getClass().getName());
throw new SpelException(SpelMessages.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetObjectTypeDescriptor);
}
@ -101,27 +93,38 @@ public class Indexer extends SpelNodeImpl {
@SuppressWarnings("unchecked")
@Override
public void setValue(ExpressionState state, Object newValue) throws EvaluationException {
Object ctx = state.getActiveContextObject();
Object index = getChild(0).getValueInternal(state);
TypedValue contextObject = state.getActiveContextObject();
Object targetObject = contextObject.getValue();
TypeDescriptor targetObjectTypeDescriptor = contextObject.getTypeDescriptor();
TypedValue index = getChild(0).getValueInternal(state);
// Indexing into a Map
if (ctx instanceof Map) {
((Map) ctx).put(index,newValue); // TODO missing conversion for both index and newValue
if (targetObjectTypeDescriptor.isMap()) {
Map map = (Map)targetObject;
Object possiblyConvertedKey = state.convertValue(index.getValue(),TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapKeyType()));
Object possiblyConvertedValue = state.convertValue(newValue,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapValueType()));
map.put(possiblyConvertedKey,possiblyConvertedValue);
return;
}
int idx = state.convertValue(index, INTEGER_TYPE_DESCRIPTOR);
if (ctx.getClass().isArray()) {
setArrayElement(state, ctx, idx, newValue);
} else if (ctx instanceof List) {
List<Object> c = (List<Object>) ctx;
if (targetObjectTypeDescriptor.isArray()) {
int idx = state.convertValue(index, INTEGER_TYPE_DESCRIPTOR);
setArrayElement(state, contextObject.getValue(), idx, newValue, targetObjectTypeDescriptor.getElementType());
} else if (targetObjectTypeDescriptor.isCollection()) {
int idx = state.convertValue(index, INTEGER_TYPE_DESCRIPTOR);
Collection c = (Collection) targetObject;
if (idx >= c.size()) {
throw new SpelException(SpelMessages.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
}
c.set(idx,newValue); // TODO missing conversion
if (targetObject instanceof List) {
List list = (List)targetObject;
Object possiblyConvertedValue = state.convertValue(newValue,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getElementType()));
list.set(idx,possiblyConvertedValue);
} else {
throw new SpelException(SpelMessages.INDEXING_NOT_SUPPORTED_FOR_TYPE, contextObject.getClass().getName());
}
} else {
throw new SpelException(SpelMessages.INDEXING_NOT_SUPPORTED_FOR_TYPE, ctx.getClass().getName());
throw new SpelException(SpelMessages.INDEXING_NOT_SUPPORTED_FOR_TYPE, contextObject.getClass().getName());
}
}
@ -138,44 +141,44 @@ public class Indexer extends SpelNodeImpl {
return sb.toString();
}
private void setArrayElement(ExpressionState state, Object ctx, int idx, Object newValue) throws EvaluationException {
Class<?> arrayComponentType = ctx.getClass().getComponentType();
private void setArrayElement(ExpressionState state, Object ctx, int idx, Object newValue, Class clazz) throws EvaluationException {
Class<?> arrayComponentType = clazz;
if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx;
checkAccess(array.length, idx);
array[idx] = state.convertValue(newValue, Integer.class);
array[idx] = state.convertValue(newValue, INTEGER_TYPE_DESCRIPTOR);
} else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx);
array[idx] = state.convertValue(newValue, Boolean.class);
array[idx] = state.convertValue(newValue, BOOLEAN_TYPE_DESCRIPTOR);
} else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx;
checkAccess(array.length, idx);
array[idx] = state.convertValue(newValue, Character.class);
array[idx] = state.convertValue(newValue, CHARACTER_TYPE_DESCRIPTOR);
} else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx;
checkAccess(array.length, idx);
array[idx] = state.convertValue(newValue, Long.class);
array[idx] = state.convertValue(newValue, LONG_TYPE_DESCRIPTOR);
} else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx;
checkAccess(array.length, idx);
array[idx] = state.convertValue(newValue, Short.class);
array[idx] = state.convertValue(newValue, SHORT_TYPE_DESCRIPTOR);
} else if (arrayComponentType == Double.TYPE) {
double[] array = (double[]) ctx;
checkAccess(array.length, idx);
array[idx] = state.convertValue(newValue, Double.class);
array[idx] = state.convertValue(newValue, DOUBLE_TYPE_DESCRIPTOR);
} else if (arrayComponentType == Float.TYPE) {
float[] array = (float[]) ctx;
checkAccess(array.length, idx);
array[idx] = state.convertValue(newValue, Float.class);
array[idx] = state.convertValue(newValue, FLOAT_TYPE_DESCRIPTOR);
} else if (arrayComponentType == Byte.TYPE) {
byte[] array = (byte[]) ctx;
checkAccess(array.length, idx);
array[idx] = state.convertValue(newValue, Byte.class);
array[idx] = state.convertValue(newValue, BYTE_TYPE_DESCRIPTOR);
} else {
Object[] array = (Object[]) ctx;
checkAccess(array.length, idx);
array[idx] = state.convertValue(newValue, arrayComponentType);
array[idx] = state.convertValue(newValue, TypeDescriptor.valueOf(clazz));
}
}

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.TypedValue;
/**
* Expression language AST node that represents an integer literal.
@ -26,15 +27,15 @@ import org.antlr.runtime.Token;
*/
public class IntLiteral extends Literal {
private final Integer value;
private final TypedValue value;
IntLiteral(Token payload, int value) {
super(payload);
this.value = value;
this.value = new TypedValue(value, INTEGER_TYPE_DESCRIPTOR);
}
@Override
public Integer getLiteralValue() {
public TypedValue getLiteralValue() {
return this.value;
}

View File

@ -17,6 +17,7 @@
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;
@ -34,16 +35,16 @@ public abstract class Literal extends SpelNodeImpl {
super(payload);
}
public abstract Object getLiteralValue();
public abstract TypedValue getLiteralValue();
@Override
public final Object getValueInternal(ExpressionState state) throws SpelException {
public final TypedValue getValueInternal(ExpressionState state) throws SpelException {
return getLiteralValue();
}
@Override
public String toString() {
return getLiteralValue().toString();
return getLiteralValue().getValue().toString();
}
@Override

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.TypedValue;
/**
* Expression language AST node that represents a long integer literal.
@ -26,15 +27,15 @@ import org.antlr.runtime.Token;
*/
public class LongLiteral extends Literal {
private final Long value;
private final TypedValue value;
LongLiteral(Token payload, long value) {
super(payload);
this.value = value;
this.value = new TypedValue(value, LONG_TYPE_DESCRIPTOR);
}
@Override
public Long getLiteralValue() {
public TypedValue getLiteralValue() {
return this.value;
}

View File

@ -19,12 +19,12 @@ package org.springframework.expression.spel.ast;
import java.util.List;
import org.antlr.runtime.Token;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
@ -48,11 +48,11 @@ public class MethodReference extends SpelNodeImpl {
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object currentContext = state.getActiveContextObject();
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue currentContext = state.getActiveContextObject();
Object[] arguments = new Object[getChildCount()];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = getChild(i).getValueInternal(state);
arguments[i] = getChild(i).getValueInternal(state).getValue();
}
if (currentContext == null) {
throw new SpelException(getCharPositionInLine(), SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT,
@ -63,7 +63,7 @@ public class MethodReference extends SpelNodeImpl {
if (executorToUse != null) {
try {
return executorToUse.execute(
state.getEvaluationContext(), state.getActiveContextObject(), arguments);
state.getEvaluationContext(), state.getActiveContextObject().getValue(), arguments);
}
catch (AccessException ae) {
// this is OK - it may have gone stale due to a class change,
@ -77,7 +77,7 @@ public class MethodReference extends SpelNodeImpl {
this.cachedExecutor = executorToUse;
try {
return executorToUse.execute(
state.getEvaluationContext(), state.getActiveContextObject(), arguments);
state.getEvaluationContext(), state.getActiveContextObject().getValue(), arguments);
}
catch (AccessException ae) {
throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_METHOD_INVOCATION,
@ -139,7 +139,8 @@ public class MethodReference extends SpelNodeImpl {
protected MethodExecutor findAccessorForMethod(String name, Class<?>[] argumentTypes, ExpressionState state)
throws SpelException {
Object contextObject = state.getActiveContextObject();
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,

View File

@ -17,6 +17,8 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.TypedValue;
/**
* @author Andy Clement
@ -24,13 +26,15 @@ import org.antlr.runtime.Token;
*/
public class NullLiteral extends Literal {
private static final TypedValue NULL_TYPED_VALUE = new TypedValue(null,TypeDescriptor.NULL_TYPE_DESCRIPTOR);
public NullLiteral(Token payload) {
super(payload);
}
@Override
public String getLiteralValue() {
return null;
public TypedValue getLiteralValue() {
return NULL_TYPED_VALUE;
}
@Override

View File

@ -18,8 +18,10 @@ package org.springframework.expression.spel.ast;
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;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Represents the boolean AND operation.
@ -39,7 +41,7 @@ public class OperatorAnd extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
boolean leftValue;
boolean rightValue;
@ -52,7 +54,7 @@ public class OperatorAnd extends Operator {
}
if (leftValue == false) {
return false; // no need to evaluate right operand
return BooleanTypedValue.forValue(false); // no need to evaluate right operand
}
try {
@ -63,7 +65,7 @@ public class OperatorAnd extends Operator {
throw ee;
}
return /* leftValue && */rightValue;
return /* leftValue && */BooleanTypedValue.forValue(rightValue);
}
}

View File

@ -24,6 +24,7 @@ import org.springframework.expression.TypeComparator;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Represents the between operator. The left operand to between must be a single value and the right operand must be a
@ -52,9 +53,9 @@ public class OperatorBetween extends Operator {
* @throws EvaluationException if there is a problem evaluating the expression
*/
@Override
public Boolean getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state);
Object right = getRightOperand().getValueInternal(state);
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (!(right instanceof List) || ((List<?>) right).size() != 2) {
throw new SpelException(getRightOperand().getCharPositionInLine(),
SpelMessages.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST);
@ -64,7 +65,7 @@ public class OperatorBetween extends Operator {
Object high = l.get(1);
TypeComparator comparator = state.getTypeComparator();
try {
return (comparator.compare(left, low) >= 0 && comparator.compare(left, high) <= 0);
return BooleanTypedValue.forValue((comparator.compare(left, low) >= 0 && comparator.compare(left, high) <= 0));
} catch (SpelException ex) {
ex.setPosition(getCharPositionInLine());
throw ex;

View File

@ -17,8 +17,10 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
@ -39,26 +41,22 @@ public class OperatorDivide extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state);
Object operandTwo = getRightOperand().getValueInternal(state);
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
Object operandTwo = getRightOperand().getValueInternal(state).getValue();
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() / op2.doubleValue();
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() / op2.floatValue();
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() / op2.longValue();
}
else { // TODO what about non-int result of the division?
return op1.intValue() / op2.intValue();
return new TypedValue(op1.doubleValue() / op2.doubleValue(), DOUBLE_TYPE_DESCRIPTOR);
} else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() / op2.longValue(), LONG_TYPE_DESCRIPTOR);
} else { // TODO what about non-int result of the division?
return new TypedValue(op1.intValue() / op2.intValue(), INTEGER_TYPE_DESCRIPTOR);
}
}
return state.operate(Operation.DIVIDE, operandOne, operandTwo);
Object result = state.operate(Operation.DIVIDE, operandOne, operandTwo);
return new TypedValue(result,TypeDescriptor.forObject(result));
}
}

View File

@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Implements equality operator.
@ -38,26 +39,21 @@ public class OperatorEquality extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state);
Object right = getRightOperand().getValueInternal(state);
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() == op2.doubleValue();
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() == op2.floatValue();
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() == op2.longValue();
}
else {
return op1.intValue() == op2.intValue();
return BooleanTypedValue.forValue(op1.doubleValue() == op2.doubleValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return BooleanTypedValue.forValue(op1.longValue() == op2.longValue());
} else {
return BooleanTypedValue.forValue(op1.intValue() == op2.intValue());
}
}
return state.getTypeComparator().compare(left, right) == 0;
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) == 0);
}
}

View File

@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Implements greater-than operator.
@ -38,26 +39,26 @@ public class OperatorGreaterThan extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state);
Object right = getRightOperand().getValueInternal(state);
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() > op2.doubleValue();
return BooleanTypedValue.forValue(op1.doubleValue() > op2.doubleValue());
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() > op2.floatValue();
return BooleanTypedValue.forValue(op1.floatValue() > op2.floatValue());
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() > op2.longValue();
return BooleanTypedValue.forValue(op1.longValue() > op2.longValue());
}
else {
return op1.intValue() > op2.intValue();
return BooleanTypedValue.forValue(op1.intValue() > op2.intValue());
}
}
return state.getTypeComparator().compare(left, right) > 0;
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) > 0);
}
}

View File

@ -13,12 +13,12 @@
* 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.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Implements greater-than-or-equal operator.
@ -38,26 +38,26 @@ public class OperatorGreaterThanOrEqual extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state);
Object right = getRightOperand().getValueInternal(state);
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() >= op2.doubleValue();
return BooleanTypedValue.forValue(op1.doubleValue() >= op2.doubleValue());
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() >= op2.floatValue();
return BooleanTypedValue.forValue(op1.floatValue() >= op2.floatValue());
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() >= op2.longValue();
return BooleanTypedValue.forValue( op1.longValue() >= op2.longValue());
}
else {
return op1.intValue() >= op2.intValue();
return BooleanTypedValue.forValue(op1.intValue() >= op2.intValue());
}
}
return state.getTypeComparator().compare(left, right) >= 0;
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) >= 0);
}
}

View File

@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Implements the not-equal operator.
@ -38,26 +39,21 @@ public class OperatorInequality extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state);
Object right = getRightOperand().getValueInternal(state);
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() != op2.doubleValue();
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() != op2.floatValue();
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() != op2.longValue();
}
else {
return op1.intValue() != op2.intValue();
return BooleanTypedValue.forValue(op1.doubleValue() != op2.doubleValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return BooleanTypedValue.forValue(op1.longValue() != op2.longValue());
} else {
return BooleanTypedValue.forValue(op1.intValue() != op2.intValue());
}
}
return state.getTypeComparator().compare(left, right) != 0;
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) != 0);
}
}

View File

@ -17,11 +17,12 @@
package org.springframework.expression.spel.ast;
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;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* The operator 'instanceof' checks if an object is of the class specified in the right hand operand,
@ -49,19 +50,21 @@ public class OperatorInstanceof extends Operator {
* @throws EvaluationException if there is a problem evaluating the expression
*/
@Override
public Boolean getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state);
Object right = getRightOperand().getValueInternal(state);
if (left == null) {
return false; // null is not an instanceof anything
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue left = getLeftOperand().getValueInternal(state);
TypedValue right = getRightOperand().getValueInternal(state);
Object leftValue = left.getValue();
Object rightValue = right.getValue();
if (leftValue == null) {
return BooleanTypedValue.False; // null is not an instanceof anything
}
if (right == null || !(right instanceof Class<?>)) {
if (rightValue == null || !(rightValue instanceof Class<?>)) {
throw new SpelException(getRightOperand().getCharPositionInLine(),
SpelMessages.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND,
(right == null ? "null" : right.getClass().getName()));
(rightValue == null ? "null" : rightValue.getClass().getName()));
}
Class<?> rightClass = (Class<?>) right;
return rightClass.isAssignableFrom(left.getClass());
Class<?> rightClass = (Class<?>) rightValue;
return BooleanTypedValue.forValue(rightClass.isAssignableFrom(leftValue.getClass()));
}
}

View File

@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Implements the less-than operator.
@ -38,26 +39,26 @@ public class OperatorLessThan extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state);
Object right = getRightOperand().getValueInternal(state);
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() < op2.doubleValue();
return BooleanTypedValue.forValue(op1.doubleValue() < op2.doubleValue());
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() < op2.floatValue();
return BooleanTypedValue.forValue(op1.floatValue() < op2.floatValue());
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() < op2.longValue();
return BooleanTypedValue.forValue(op1.longValue() < op2.longValue());
}
else {
return op1.intValue() < op2.intValue();
return BooleanTypedValue.forValue(op1.intValue() < op2.intValue());
}
}
return state.getTypeComparator().compare(left, right) < 0;
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) < 0);
}
}

View File

@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Implements the less-than-or-equal operator.
@ -33,26 +34,26 @@ public class OperatorLessThanOrEqual extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state);
Object right = getRightOperand().getValueInternal(state);
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() <= op2.doubleValue();
return BooleanTypedValue.forValue(op1.doubleValue() <= op2.doubleValue());
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() <= op2.floatValue();
return BooleanTypedValue.forValue(op1.floatValue() <= op2.floatValue());
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() <= op2.longValue();
return BooleanTypedValue.forValue(op1.longValue() <= op2.longValue());
}
else {
return op1.intValue() <= op2.intValue();
return BooleanTypedValue.forValue(op1.intValue() <= op2.intValue());
}
}
return state.getTypeComparator().compare(left, right) <= 0;
return BooleanTypedValue.forValue( state.getTypeComparator().compare(left, right) <= 0);
}
@Override

View File

@ -25,6 +25,7 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Implements the matches operator. Matches takes two operands. The first is a string and the second is a java regex. It
@ -51,11 +52,11 @@ public class OperatorMatches extends Operator {
* @throws EvaluationException if there is a problem evaluating the expression (e.g. the regex is invalid)
*/
@Override
public Boolean getValueInternal(ExpressionState state) throws EvaluationException {
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
Object left = leftOp.getValue(state, String.class);
Object right = getRightOperand().getValueInternal(state);
Object right = getRightOperand().getValueInternal(state).getValue();
try {
if (!(left instanceof String)) {
throw new SpelException(leftOp.getCharPositionInLine(),
@ -67,7 +68,7 @@ public class OperatorMatches extends Operator {
}
Pattern pattern = Pattern.compile((String) right);
Matcher matcher = pattern.matcher((String) left);
return matcher.matches();
return BooleanTypedValue.forValue(matcher.matches());
}
catch (PatternSyntaxException pse) {
throw new SpelException(rightOp.getCharPositionInLine(), pse, SpelMessages.INVALID_PATTERN, right);

View File

@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
@ -49,45 +50,45 @@ public class OperatorMinus extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
if (rightOp == null) {// If only one operand, then this is unary minus
Object left = leftOp.getValueInternal(state);
Object left = leftOp.getValueInternal(state).getValue();
if (left instanceof Number) {
Number n = (Number) left;
if (left instanceof Double) {
return 0 - n.doubleValue();
return new TypedValue(0 - n.doubleValue(),DOUBLE_TYPE_DESCRIPTOR);
}
else if (left instanceof Float) {
return 0 - n.floatValue();
return new TypedValue(0 - n.floatValue(),FLOAT_TYPE_DESCRIPTOR);
}
else if (left instanceof Long) {
return 0 - n.longValue();
return new TypedValue(0 - n.longValue(),LONG_TYPE_DESCRIPTOR);
}
else {
return 0 - n.intValue();
return new TypedValue(0 - n.intValue(),INTEGER_TYPE_DESCRIPTOR);
}
}
throw new SpelException(SpelMessages.CANNOT_NEGATE_TYPE, left.getClass().getName());
}
else {
Object left = leftOp.getValueInternal(state);
Object right = rightOp.getValueInternal(state);
Object left = leftOp.getValueInternal(state).getValue();
Object right = rightOp.getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() - op2.doubleValue();
return new TypedValue(op1.doubleValue() - op2.doubleValue(),DOUBLE_TYPE_DESCRIPTOR);
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() - op2.floatValue();
return new TypedValue(op1.floatValue() - op2.floatValue(),FLOAT_TYPE_DESCRIPTOR);
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() - op2.longValue();
return new TypedValue(op1.longValue() - op2.longValue(),LONG_TYPE_DESCRIPTOR);
}
else {
return op1.intValue() - op2.intValue();
return new TypedValue(op1.intValue() - op2.intValue(),INTEGER_TYPE_DESCRIPTOR);
}
}
return state.operate(Operation.SUBTRACT, left, right);

View File

@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
@ -39,23 +40,23 @@ public class OperatorModulus extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state);
Object operandTwo = getRightOperand().getValueInternal(state);
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
Object operandTwo = getRightOperand().getValueInternal(state).getValue();
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() % op2.doubleValue();
return new TypedValue(op1.doubleValue() % op2.doubleValue(),DOUBLE_TYPE_DESCRIPTOR);
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() % op2.floatValue();
return new TypedValue(op1.floatValue() % op2.floatValue(),FLOAT_TYPE_DESCRIPTOR);
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() % op2.longValue();
return new TypedValue(op1.longValue() % op2.longValue(),LONG_TYPE_DESCRIPTOR);
}
else {
return op1.intValue() % op2.intValue();
return new TypedValue(op1.intValue() % op2.intValue(),INTEGER_TYPE_DESCRIPTOR);
}
}
return state.operate(Operation.MODULUS, operandOne, operandTwo);

View File

@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
@ -50,23 +51,23 @@ public class OperatorMultiply extends Operator {
* </ul>
*/
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state);
Object operandTwo = getRightOperand().getValueInternal(state);
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
Object operandTwo = getRightOperand().getValueInternal(state).getValue();
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() * op2.doubleValue();
return new TypedValue(op1.doubleValue() * op2.doubleValue(),DOUBLE_TYPE_DESCRIPTOR);
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() * op2.floatValue();
return new TypedValue(op1.floatValue() * op2.floatValue(),FLOAT_TYPE_DESCRIPTOR);
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() * op2.longValue();
return new TypedValue(op1.longValue() * op2.longValue(),LONG_TYPE_DESCRIPTOR);
}
else {
return op1.intValue() * op2.intValue();
return new TypedValue(op1.intValue() * op2.intValue(),INTEGER_TYPE_DESCRIPTOR);
}
}
else if (operandOne instanceof String && operandTwo instanceof Integer) {
@ -75,7 +76,7 @@ public class OperatorMultiply extends Operator {
for (int i = 0; i < repeats; i++) {
result.append(operandOne);
}
return result.toString();
return new TypedValue(result.toString(),STRING_TYPE_DESCRIPTOR);
}
return state.operate(Operation.MULTIPLY, operandOne, operandTwo);
}

View File

@ -20,6 +20,7 @@ import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Represents a NOT operation.
@ -34,10 +35,10 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
try {
boolean value = state.convertValue(getChild(0).getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
return !value;
return BooleanTypedValue.forValue(!value);
}
catch (SpelException see) {
see.setPosition(getChild(0).getCharPositionInLine());

View File

@ -20,6 +20,7 @@ import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Represents the boolean OR operation.
@ -39,7 +40,7 @@ public class OperatorOr extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
boolean leftValue;
boolean rightValue;
try {
@ -51,7 +52,7 @@ public class OperatorOr extends Operator {
}
if (leftValue == true) {
return true; // no need to evaluate right operand
return BooleanTypedValue.True; // no need to evaluate right operand
}
try {
@ -62,7 +63,7 @@ public class OperatorOr extends Operator {
throw see;
}
return leftValue || rightValue;
return BooleanTypedValue.forValue(leftValue || rightValue);
}
}

View File

@ -17,9 +17,9 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
@ -33,46 +33,46 @@ public class OperatorPlus extends Operator {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
if (rightOp == null) { // If only one operand, then this is unary plus
Object operandOne = leftOp.getValueInternal(state);
Object operandOne = leftOp.getValueInternal(state).getValue();
if (operandOne instanceof Number) {
return ((Number) operandOne).intValue();
return new TypedValue(((Number) operandOne).intValue(),INTEGER_TYPE_DESCRIPTOR);
}
return state.operate(Operation.ADD, operandOne, null);
}
else {
Object operandOne = leftOp.getValueInternal(state);
Object operandTwo = rightOp.getValueInternal(state);
Object operandOne = leftOp.getValueInternal(state).getValue();
Object operandTwo = rightOp.getValueInternal(state).getValue();
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() + op2.doubleValue();
return new TypedValue(op1.doubleValue() + op2.doubleValue(),DOUBLE_TYPE_DESCRIPTOR);
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() + op2.floatValue();
return new TypedValue(op1.floatValue() + op2.floatValue(),FLOAT_TYPE_DESCRIPTOR);
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() + op2.longValue();
return new TypedValue(op1.longValue() + op2.longValue(),LONG_TYPE_DESCRIPTOR);
}
else { // TODO what about overflow?
return op1.intValue() + op2.intValue();
return new TypedValue(op1.intValue() + op2.intValue(),INTEGER_TYPE_DESCRIPTOR);
}
}
else if (operandOne instanceof String && operandTwo instanceof String) {
return new StringBuilder((String) operandOne).append((String) operandTwo).toString();
return new TypedValue(new StringBuilder((String) operandOne).append((String) operandTwo).toString(),STRING_TYPE_DESCRIPTOR);
}
else if (operandOne instanceof String && operandTwo instanceof Integer) {
String l = (String) operandOne;
Integer i = (Integer) operandTwo;
// implements character + int (ie. a + 1 = b)
if (l.length() == 1) {
return Character.toString((char) (l.charAt(0) + i));
return new TypedValue(Character.toString((char) (l.charAt(0) + i)),STRING_TYPE_DESCRIPTOR);
}
return new StringBuilder(l).append(i).toString();
return new TypedValue(new StringBuilder(l).append(i).toString(),STRING_TYPE_DESCRIPTOR);
}
return state.operate(Operation.ADD, operandOne, operandTwo);
}

View File

@ -17,9 +17,10 @@
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;
import org.springframework.expression.spel.ExpressionState;
/**
* PlaceHolder nodes are created for tokens that come through from the grammar purely to give us additional positional
@ -35,7 +36,7 @@ public class Placeholder extends SpelNodeImpl {
}
@Override
public String getValueInternal(ExpressionState state) throws SpelException {
public TypedValue getValueInternal(ExpressionState state) throws SpelException {
throw new SpelException(getCharPositionInLine(), SpelMessages.PLACEHOLDER_SHOULD_NEVER_BE_EVALUATED);
}

View File

@ -22,8 +22,9 @@ import java.util.List;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
@ -43,9 +44,12 @@ public class Projection extends SpelNodeImpl {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object operand = state.getActiveContextObject();
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue op = state.getActiveContextObject();
Object operand = op.getValue();
TypeDescriptor operandTypeDescriptor = op.getTypeDescriptor();
// When the input is a map, we push a special context object on the stack
// before calling the specified operation. This special context object
// has two fields 'key' and 'value' that refer to the map entries key
@ -56,21 +60,21 @@ public class Projection extends SpelNodeImpl {
List<Object> result = new ArrayList<Object>();
for (Object k : mapdata.keySet()) {
try {
state.pushActiveContextObject(new KeyValuePair(k, mapdata.get(k)));
state.pushActiveContextObject(new TypedValue(new KeyValuePair(k, mapdata.get(k)),TypeDescriptor.valueOf(KeyValuePair.class)));
result.add(getChild(0).getValueInternal(state));
} finally {
state.popActiveContextObject();
}
}
return result;
} else if (operand instanceof Collection) {
return new TypedValue(result,TypeDescriptor.valueOf(Map.class)); // TODO unable to build correct type descriptor
} else if (operand instanceof List) {
List<Object> data = new ArrayList<Object>();
data.addAll((Collection<?>) operand);
List<Object> result = new ArrayList<Object>();
int idx = 0;
for (Object element : data) {
try {
state.pushActiveContextObject(element);
state.pushActiveContextObject(new TypedValue(element,TypeDescriptor.valueOf(op.getTypeDescriptor().getType())));
state.enterScope("index", idx);
result.add(getChild(0).getValueInternal(state));
} finally {
@ -79,7 +83,7 @@ public class Projection extends SpelNodeImpl {
}
idx++;
}
return result;
return new TypedValue(result,op.getTypeDescriptor());
} else {
throw new SpelException(SpelMessages.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName());
}

View File

@ -23,6 +23,7 @@ import org.antlr.runtime.Token;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
@ -49,7 +50,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
@Override
public Object getValueInternal(ExpressionState state) throws SpelException {
public TypedValue getValueInternal(ExpressionState state) throws SpelException {
return readProperty(state, this.name);
}
@ -75,14 +76,14 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
* @return the value of the property
* @throws SpelException if any problem accessing the property or it cannot be found
*/
private Object readProperty(ExpressionState state, String name) throws SpelException {
Object contextObject = state.getActiveContextObject();
private TypedValue readProperty(ExpressionState state, String name) throws SpelException {
TypedValue contextObject = state.getActiveContextObject();
EvaluationContext eContext = state.getEvaluationContext();
PropertyAccessor accessorToUse = this.cachedReadAccessor;
if (accessorToUse != null) {
try {
return accessorToUse.read(state.getEvaluationContext(), contextObject, name);
return accessorToUse.read(state.getEvaluationContext(), contextObject.getValue(), name);
}
catch (AccessException ae) {
// this is OK - it may have gone stale due to a class change,
@ -91,7 +92,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
}
Class<?> contextObjectClass = getObjectClass(contextObject);
Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, state);
// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then
@ -100,9 +101,9 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
if (accessorsToTry != null) {
try {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canRead(eContext, contextObject, name)) {
if (accessor.canRead(eContext, contextObject.getValue(), name)) {
this.cachedReadAccessor = accessor;
return accessor.read(eContext, contextObject, name);
return accessor.read(eContext, contextObject.getValue(), name);
}
}
}
@ -115,13 +116,13 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
private void writeProperty(ExpressionState state, String name, Object newValue) throws SpelException {
Object contextObject = state.getActiveContextObject();
TypedValue contextObject = state.getActiveContextObject();
EvaluationContext eContext = state.getEvaluationContext();
PropertyAccessor accessorToUse = this.cachedWriteAccessor;
if (accessorToUse != null) {
try {
accessorToUse.write(state.getEvaluationContext(), contextObject, name, newValue);
accessorToUse.write(state.getEvaluationContext(), contextObject.getValue(), name, newValue);
return;
}
catch (AccessException ae) {
@ -131,15 +132,15 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
}
Class<?> contextObjectClass = getObjectClass(contextObject);
Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, state);
if (accessorsToTry != null) {
try {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canWrite(eContext, contextObject, name)) {
if (accessor.canWrite(eContext, contextObject.getValue(), name)) {
this.cachedWriteAccessor = accessor;
accessor.write(eContext, contextObject, name, newValue);
accessor.write(eContext, contextObject.getValue(), name, newValue);
return;
}
}
@ -153,7 +154,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
public boolean isWritableProperty(String name, ExpressionState state) throws SpelException {
Object contextObject = state.getActiveContextObject();
Object contextObject = state.getActiveContextObject().getValue();
EvaluationContext eContext = state.getEvaluationContext();
if (contextObject == null) {
throw new SpelException(SpelMessages.ATTEMPTED_PROPERTY_FIELD_REF_ON_NULL_CONTEXT_OBJECT, name);

View File

@ -17,8 +17,8 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
@ -39,7 +39,7 @@ public class QualifiedIdentifier extends SpelNodeImpl {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
// Cache the concatenation of child identifiers
if (this.value == null) {
StringBuilder sb = new StringBuilder();
@ -47,11 +47,11 @@ public class QualifiedIdentifier extends SpelNodeImpl {
if (i > 0) {
sb.append(".");
}
sb.append(getChild(i).getValueInternal(state));
sb.append(getChild(i).getValueInternal(state).getValue());
}
this.value = sb.toString();
}
return this.value;
return new TypedValue(this.value,STRING_TYPE_DESCRIPTOR);
}
@Override

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.TypedValue;
/**
* @author Andy Clement
@ -24,15 +25,15 @@ import org.antlr.runtime.Token;
*/
public class RealLiteral extends Literal {
private final Double value;
private final TypedValue value;
public RealLiteral(Token payload) {
super(payload);
value = Double.parseDouble(payload.getText());
value = new TypedValue(Double.parseDouble(payload.getText()),DOUBLE_TYPE_DESCRIPTOR);
}
@Override
public Double getLiteralValue() {
public TypedValue getLiteralValue() {
return this.value;
}

View File

@ -22,8 +22,9 @@ import java.util.List;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
@ -51,15 +52,17 @@ public class Selection extends SpelNodeImpl {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object operand = state.getActiveContextObject();
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue op = state.getActiveContextObject();
Object operand = op.getValue();
SpelNodeImpl selectionCriteria = getChild(0);
if (operand instanceof Map) {
Map<?, ?> mapdata = (Map<?, ?>) operand;
List<Object> result = new ArrayList<Object>();
for (Object k : mapdata.keySet()) {
try {
Object kvpair = new KeyValuePair(k, mapdata.get(k));
TypedValue kvpair = new TypedValue(new KeyValuePair(k, mapdata.get(k)),TypeDescriptor.valueOf(KeyValuePair.class));
state.pushActiveContextObject(kvpair);
Object o = selectionCriteria.getValueInternal(state);
if (o instanceof Boolean) {
@ -79,10 +82,10 @@ public class Selection extends SpelNodeImpl {
return null;
}
if (variant == LAST) {
return result.get(result.size() - 1);
return new TypedValue(result.get(result.size() - 1),TypeDescriptor.valueOf(op.getTypeDescriptor().getElementType()));
}
}
return result;
return new TypedValue(result,op.getTypeDescriptor());
} else if (operand instanceof Collection) {
List<Object> data = new ArrayList<Object>();
data.addAll((Collection<?>) operand);
@ -90,13 +93,13 @@ public class Selection extends SpelNodeImpl {
int idx = 0;
for (Object element : data) {
try {
state.pushActiveContextObject(element);
state.pushActiveContextObject(new TypedValue(element,TypeDescriptor.valueOf(op.getTypeDescriptor().getElementType())));
state.enterScope("index", idx);
Object o = selectionCriteria.getValueInternal(state);
Object o = selectionCriteria.getValueInternal(state).getValue();
if (o instanceof Boolean) {
if (((Boolean) o).booleanValue() == true) {
if (variant == FIRST)
return element;
return new TypedValue(element,TypeDescriptor.valueOf(op.getTypeDescriptor().getElementType()));
result.add(element);
}
} else {
@ -113,9 +116,9 @@ public class Selection extends SpelNodeImpl {
return null;
}
if (variant == LAST) {
return result.get(result.size() - 1);
return new TypedValue(result.get(result.size() - 1),TypeDescriptor.valueOf(op.getTypeDescriptor().getElementType()));
}
return result;
return new TypedValue(result,op.getTypeDescriptor());
} else {
throw new SpelException(getCharPositionInLine(), SpelMessages.INVALID_TYPE_FOR_SELECTION,
(operand == null ? "null" : operand.getClass().getName()));

View File

@ -20,8 +20,8 @@ import java.io.Serializable;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.common.ExpressionUtils;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
@ -36,10 +36,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
* @author Andy Clement
* @since 3.0
*/
public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Serializable {
protected static TypeDescriptor BOOLEAN_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Boolean.class);
protected static TypeDescriptor INTEGER_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Integer.class);
public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Serializable, CommonTypeDescriptors {
/**
* The Antlr parser uses this constructor to build SpelNodes.
@ -51,7 +48,7 @@ public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Seria
public final Object getValue(ExpressionState expressionState) throws EvaluationException {
if (expressionState != null) {
return getValueInternal(expressionState);
return getValueInternal(expressionState).getValue();
}
else {
return getValue(new ExpressionState(new StandardEvaluationContext()));
@ -88,7 +85,7 @@ public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Seria
@SuppressWarnings("unchecked")
protected final <T> T getValue(ExpressionState state, Class<T> desiredReturnType) throws EvaluationException {
Object result = getValueInternal(state);
Object result = getValueInternal(state).getValue();
if (result != null && desiredReturnType != null) {
Class<?> resultType = result.getClass();
if (desiredReturnType.isAssignableFrom(resultType)) {
@ -105,7 +102,7 @@ public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Seria
}
public abstract Object getValueInternal(ExpressionState expressionState) throws EvaluationException;
public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;
public abstract String toStringAST();

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.TypedValue;
/**
* @author Andy Clement
@ -25,24 +26,24 @@ import org.antlr.runtime.Token;
*/
public class StringLiteral extends Literal {
private final String value;
private final TypedValue value;
public StringLiteral(Token payload) {
super(payload);
String val = payload.getText();
// TODO should these have been skipped being created by the parser rules? or not?
val = val.substring(1, val.length() - 1);
this.value = val.replaceAll("''", "'");
this.value = new TypedValue(val.replaceAll("''", "'"),STRING_TYPE_DESCRIPTOR);
}
@Override
public String getLiteralValue() {
public TypedValue getLiteralValue() {
return this.value;
}
@Override
public String toString() {
return "'" + getLiteralValue() + "'";
return "'" + getLiteralValue().getValue() + "'";
}
}

View File

@ -17,8 +17,8 @@
package org.springframework.expression.spel.ast;
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;
@ -42,7 +42,7 @@ public class Ternary extends SpelNodeImpl {
* executing the chosen alternative
*/
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Boolean value = getChild(0).getValue(state, Boolean.class);
try {
if (Boolean.TRUE.equals(value)) {

View File

@ -17,8 +17,8 @@
package org.springframework.expression.spel.ast;
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;
@ -34,17 +34,17 @@ public class TypeReference extends SpelNodeImpl {
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
// TODO possible optimization here if we cache the discovered type reference, but can we do that?
String typename = (String) getChild(0).getValueInternal(state);
String typename = (String) getChild(0).getValueInternal(state).getValue();
if (typename.indexOf(".") == -1 && Character.isLowerCase(typename.charAt(0))) {
TypeCode tc = TypeCode.forName(typename);
if (tc != TypeCode.OBJECT) {
// it is a primitive type
return tc.getType();
return new TypedValue(tc.getType(),CLASS_TYPE_DESCRIPTOR);
}
}
return state.findType(typename);
return new TypedValue(state.findType(typename),CLASS_TYPE_DESCRIPTOR);
}
@Override

View File

@ -17,7 +17,7 @@
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;
@ -44,14 +44,14 @@ public class VariableReference extends SpelNodeImpl {
@Override
public Object getValueInternal(ExpressionState state) throws SpelException {
public TypedValue getValueInternal(ExpressionState state) throws SpelException {
if (this.name.equals(THIS)) {
return state.getActiveContextObject();
}
if (this.name.equals(ROOT)) {
return state.getRootContextObject();
}
Object result = state.lookupVariable(this.name);
TypedValue result = state.lookupVariable(this.name);
if (result == null) {
throw new SpelException(getCharPositionInLine(), SpelMessages.VARIABLE_NOT_FOUND, this.name);
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.support;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ast.SpelNodeImpl;
/**
* @author Andy Clement
* @since 3.0
*/
public class BooleanTypedValue extends TypedValue {
public static final BooleanTypedValue True = new BooleanTypedValue(true);
public static final BooleanTypedValue False = new BooleanTypedValue(false);
private BooleanTypedValue(boolean b) {
super(b,SpelNodeImpl.BOOLEAN_TYPE_DESCRIPTOR);
}
public static BooleanTypedValue forValue(boolean b) {
if (b) {
return True;
} else {
return False;
}
}
}

View File

@ -18,9 +18,11 @@ package org.springframework.expression.spel.support;
import java.lang.reflect.Constructor;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.ConstructorExecutor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.TypedValue;
/**
* A simple ConstructorExecutor implementation that runs a constructor using reflective invocation.
@ -42,7 +44,7 @@ class ReflectiveConstructorExecutor implements ConstructorExecutor {
this.argsRequiringConversion = argsRequiringConversion;
}
public Object execute(EvaluationContext context, Object... arguments) throws AccessException {
public TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException {
try {
if (argsRequiringConversion != null && arguments != null) {
ReflectionHelper.convertArguments(c.getParameterTypes(), c.isVarArgs(),
@ -54,7 +56,7 @@ class ReflectiveConstructorExecutor implements ConstructorExecutor {
if (!c.isAccessible()) {
c.setAccessible(true);
}
return c.newInstance(arguments);
return new TypedValue(c.newInstance(arguments),TypeDescriptor.valueOf(c.getClass()));
}
catch (Exception ex) {
throw new AccessException("Problem invoking constructor: " + c, ex);

View File

@ -18,9 +18,12 @@ package org.springframework.expression.spel.support;
import java.lang.reflect.Method;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.TypedValue;
import org.springframework.util.ReflectionUtils;
/**
@ -42,7 +45,7 @@ class ReflectiveMethodExecutor implements MethodExecutor {
}
public Object execute(EvaluationContext context, Object target, Object... arguments) throws AccessException {
public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException {
try {
if (this.argsRequiringConversion != null && arguments != null) {
ReflectionHelper.convertArguments(this.method.getParameterTypes(), this.method.isVarArgs(),
@ -52,7 +55,7 @@ class ReflectiveMethodExecutor implements MethodExecutor {
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.method.getParameterTypes(), arguments);
}
ReflectionUtils.makeAccessible(this.method);
return this.method.invoke(target, arguments);
return new TypedValue(this.method.invoke(target, arguments), new TypeDescriptor(new MethodParameter(method,-1)));
}
catch (Exception ex) {
throw new AccessException("Problem invoking method: " + this.method, ex);

View File

@ -30,6 +30,7 @@ import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@ -85,7 +86,7 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
return false;
}
public Object read(EvaluationContext context, Object target, String name) throws AccessException {
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
if (target == null) {
return null;
}
@ -95,7 +96,7 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
if (target instanceof Class) {
throw new AccessException("Cannot access length on array class itself");
}
return Array.getLength(target);
return new TypedValue(Array.getLength(target),TypeDescriptor.valueOf(Integer.TYPE));
}
CacheKey cacheKey = new CacheKey(type, name);
@ -113,7 +114,8 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
if (method != null) {
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target);
TypeDescriptor resultTypeDescriptor = new TypeDescriptor(new MethodParameter(method,-1));
return new TypedValue(method.invoke(target),resultTypeDescriptor);
}
catch (Exception ex) {
throw new AccessException("Unable to access property '" + name + "' through getter", ex);
@ -133,7 +135,7 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
if (field != null) {
try {
ReflectionUtils.makeAccessible(field);
return field.get(target);
return new TypedValue(field.get(target),new TypeDescriptor(field));
}
catch (Exception ex) {
throw new AccessException("Unable to access field: " + name, ex);

View File

@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.MethodResolver;
@ -30,6 +31,7 @@ import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypeLocator;
import org.springframework.expression.TypedValue;
import org.springframework.util.Assert;
/**
@ -43,7 +45,7 @@ import org.springframework.util.Assert;
*/
public class StandardEvaluationContext implements EvaluationContext {
private Object rootObject;
private TypedValue rootObject;
private final Map<String, Object> variables = new HashMap<String, Object>();
@ -68,10 +70,14 @@ public class StandardEvaluationContext implements EvaluationContext {
}
public void setRootObject(Object rootObject) {
this.rootObject = rootObject;
this.rootObject = new TypedValue(rootObject,TypeDescriptor.forObject(rootObject));
}
public Object getRootObject() {
public void setRootObject(Object rootObject, TypeDescriptor typeDescriptor) {
this.rootObject = new TypedValue(rootObject,typeDescriptor);
}
public TypedValue getRootObject() {
return this.rootObject;
}

View File

@ -23,12 +23,14 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@ -235,6 +237,7 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase {
private static class FruitColourAccessor implements PropertyAccessor {
private static Map<String,Color> propertyMap = new HashMap<String,Color>();
private static TypeDescriptor mapElementTypeDescriptor = TypeDescriptor.valueOf(Color.class);
static {
propertyMap.put("banana",Color.yellow);
@ -253,8 +256,8 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase {
return propertyMap.containsKey(name);
}
public Object read(EvaluationContext context, Object target, String name) throws AccessException {
return propertyMap.get(name);
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(propertyMap.get(name),mapElementTypeDescriptor);
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
@ -292,8 +295,8 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase {
return propertyMap.containsKey(name);
}
public Object read(EvaluationContext context, Object target, String name) throws AccessException {
return propertyMap.get(name);
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(propertyMap.get(name),TypeDescriptor.valueOf(Color.class));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {

View File

@ -22,7 +22,9 @@ import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
import org.springframework.expression.spel.ast.CommonTypeDescriptors;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
@ -67,8 +69,8 @@ public class MapAccessTests extends ExpressionTestCase {
return (((Map) target).containsKey(name));
}
public Object read(EvaluationContext context, Object target, String name) throws AccessException {
return ((Map) target).get(name);
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((Map) target).get(name),CommonTypeDescriptors.OBJECT_TYPE_DESCRIPTOR);
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {

View File

@ -16,6 +16,8 @@
package org.springframework.expression.spel;
import org.springframework.expression.spel.ast.Operator;
/**
* Tests the evaluation of expressions using relational operators.
*
@ -48,6 +50,12 @@ public class OperatorTests extends ExpressionTestCase {
evaluate("6 == 6", true, Boolean.class);
}
public void testNotEqual() {
evaluate("3 != 5", true, Boolean.class);
evaluate("5 != 3", true, Boolean.class);
evaluate("6 != 6", false, Boolean.class);
}
public void testGreaterThanOrEqual() {
evaluate("3 >= 5", false, Boolean.class);
evaluate("5 >= 3", true, Boolean.class);
@ -79,6 +87,10 @@ public class OperatorTests extends ExpressionTestCase {
evaluate("3 % 2", 1, Integer.class);
}
public void testDivide() {
evaluate("4L/2L",2L,Long.class);
}
public void testMathOperatorDivide_ConvertToDouble() {
evaluateAndAskForReturnType("8/4", new Double(2.0), Double.class);
}
@ -92,6 +104,10 @@ public class OperatorTests extends ExpressionTestCase {
// }
public void testDoubles() {
evaluate("3.0d == 5.0d", false, Boolean.class);
evaluate("3.0d == 3.0d", true, Boolean.class);
evaluate("3.0d != 5.0d", true, Boolean.class);
evaluate("3.0d != 3.0d", false, Boolean.class);
evaluate("3.0d + 5.0d", 8.0d, Double.class);
evaluate("3.0d - 5.0d", -2.0d, Double.class);
evaluate("3.0d * 5.0d", 15.0d, Double.class);
@ -100,6 +116,10 @@ public class OperatorTests extends ExpressionTestCase {
}
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);
@ -107,6 +127,17 @@ public class OperatorTests extends ExpressionTestCase {
evaluate("5.0f % 3.1f", 1.9d, Double.class);
}
public void testOperatorStrings() throws Exception {
Operator node = getOperatorNode((SpelExpression)parser.parseExpression("1==3"));
assertEquals("==",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("1!=3"));
assertEquals("!=",node.getOperatorName());
node = getOperatorNode((SpelExpression)parser.parseExpression("3/3"));
assertEquals("/",node.getOperatorName());
}
public void testMixedOperands_FloatsAndDoubles() {
evaluate("3.0d + 5.0f", 8.0d, Double.class);
evaluate("3.0D - 5.0f", -2.0d, Double.class);
@ -125,4 +156,37 @@ public class OperatorTests extends ExpressionTestCase {
evaluate("5.5D % 3", 2.5, Double.class);
}
public void testStrings() {
evaluate("'abc' == 'abc'",true,Boolean.class);
evaluate("'abc' == 'def'",false,Boolean.class);
evaluate("'abc' != 'abc'",false,Boolean.class);
evaluate("'abc' != 'def'",true,Boolean.class);
}
public void testLongs() {
evaluate("3L == 4L", false, Boolean.class);
evaluate("3L == 3L", true, Boolean.class);
evaluate("3L != 4L", true, Boolean.class);
evaluate("3L != 3L", false, Boolean.class);
}
private Operator getOperatorNode(SpelExpression e) {
SpelNode node = e.getAST();
return (Operator)findNode(node,Operator.class);
}
private SpelNode findNode(SpelNode node, Class<Operator> clazz) {
if (clazz.isAssignableFrom(node.getClass())) {
return node;
}
int childCount = node.getChildCount();
for (int i=0;i<childCount;i++) {
SpelNode possible = findNode(node.getChild(i),clazz);
if (possible!=null) {
return possible;
}
}
return null;
}
}

View File

@ -21,8 +21,6 @@ import junit.framework.TestCase;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
import org.springframework.expression.spel.ast.ConstructorReference;
import org.springframework.expression.spel.ast.PropertyOrFieldReference;
/**
* Tests the evaluation of real expressions in a real context.
@ -31,7 +29,7 @@ import org.springframework.expression.spel.ast.PropertyOrFieldReference;
*/
public class PerformanceTests extends TestCase {
public static final int ITERATIONS = 100000;
public static final int ITERATIONS = 10000;
public static final boolean report = true;
private static SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser();
@ -41,6 +39,15 @@ public class PerformanceTests extends TestCase {
long starttime = 0;
long endtime = 0;
// warmup
for (int i = 0; i < ITERATIONS; i++) {
Expression expr = parser.parseExpression("placeOfBirth.city");
if (expr == null) {
fail("Parser returned null for expression");
}
Object value = expr.getValue(eContext);
}
starttime = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
Expression expr = parser.parseExpression("placeOfBirth.city");
@ -74,6 +81,15 @@ public class PerformanceTests extends TestCase {
public void testPerformanceOfMethodAccess() throws Exception {
long starttime = 0;
long endtime = 0;
// warmup
for (int i = 0; i < ITERATIONS; i++) {
Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()");
if (expr == null) {
fail("Parser returned null for expression");
}
Object value = expr.getValue(eContext);
}
starttime = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {

View File

@ -21,7 +21,9 @@ import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
import org.springframework.expression.spel.ast.CommonTypeDescriptors;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
@ -57,7 +59,7 @@ public class PropertyAccessTests extends ExpressionTestCase {
// any 'default' ones
ctx.addPropertyAccessor(new StringyPropertyAccessor());
Expression expr = parser.parseExpression("new String('hello').flibbles");
Integer i = (Integer) expr.getValue(ctx, Integer.class);
Integer i = expr.getValue(ctx, Integer.class);
assertEquals((int) i, 7);
// The reflection one will be used for other properties...
@ -67,7 +69,7 @@ public class PropertyAccessTests extends ExpressionTestCase {
expr = parser.parseExpression("new String('hello').flibbles");
expr.setValue(ctx, 99);
i = (Integer) expr.getValue(ctx, Integer.class);
i = expr.getValue(ctx, Integer.class);
assertEquals((int) i, 99);
// Cannot set it to a string value
@ -103,10 +105,10 @@ public class PropertyAccessTests extends ExpressionTestCase {
return (name.equals("flibbles"));
}
public Object read(EvaluationContext context, Object target, String name) throws AccessException {
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
if (!name.equals("flibbles"))
throw new RuntimeException("Assertion Failed! name should be flibbles");
return flibbles;
return new TypedValue(flibbles,CommonTypeDescriptors.STRING_TYPE_DESCRIPTOR);
}
public void write(EvaluationContext context, Object target, String name, Object newValue)

View File

@ -18,6 +18,7 @@ package org.springframework.expression.spel;
import java.lang.reflect.Method;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
@ -27,6 +28,7 @@ import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
import org.springframework.expression.spel.support.ReflectionHelper;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@ -84,11 +86,11 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase {
ctx.setRootObject(null);
pAccessor.setPerson(new Person("Andy"));
value = (Boolean)expr.getValue(ctx,Boolean.class);
value = expr.getValue(ctx,Boolean.class);
assertTrue(value);
pAccessor.setPerson(new Person("Christian"));
value = (Boolean)expr.getValue(ctx,Boolean.class);
value = expr.getValue(ctx,Boolean.class);
assertFalse(value);
}
@ -210,8 +212,8 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase {
return name.equals("principal");
}
public Object read(EvaluationContext context, Object target, String name) throws AccessException {
return new Principal();
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(new Principal(),TypeDescriptor.valueOf(Principal.class));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
@ -240,8 +242,8 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase {
return name.equals("p");
}
public Object read(EvaluationContext context, Object target, String name) throws AccessException {
return activePerson;
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(activePerson,TypeDescriptor.valueOf(Person.class));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
@ -269,7 +271,7 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase {
this.tc = typeConverter;
}
public Object execute(EvaluationContext context, Object target, Object... arguments)
public TypedValue execute(EvaluationContext context, Object target, Object... arguments)
throws AccessException {
try {
Method m = HasRoleExecutor.class.getMethod("hasRole", String[].class);
@ -280,7 +282,7 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase {
if (m.isVarArgs()) {
args = ReflectionHelper.setupArgumentsForVarargsInvocation(m.getParameterTypes(), args);
}
return m.invoke(null, args);
return new TypedValue(m.invoke(null, args), new TypeDescriptor(new MethodParameter(m,-1)));
}
catch (Exception ex) {
throw new AccessException("Problem invoking hasRole", ex);

View File

@ -17,6 +17,8 @@
package org.springframework.expression.spel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
@ -77,10 +79,15 @@ public class SetValueTests extends ExpressionTestCase {
setValue("placesLivedList[0]", new PlaceOfBirth("Wien"));
}
public void testSetGenericListElementValueTypeCoersion() {
setValue("placesLivedList[0]", "Wien");
public void testSetGenericListElementValueTypeCoersionFail() {
// no type converter registered for String > PlaceOfBirth
setValueExpectError("placesLivedList[0]", "Wien");
}
public void testSetGenericListElementValueTypeCoersionOK() {
setValue("booleanList[0]", "true", Boolean.TRUE);
}
public void testSetListElementNestedValue() {
setValue("placesLived[0].city", "Wien");
}
@ -106,6 +113,46 @@ public class SetValueTests extends ExpressionTestCase {
setValue("SomeProperty", "true", Boolean.TRUE);
}
public void testAssign() throws Exception {
StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
Expression e = parse("publicName='Andy'");
assertFalse(e.isWritable(eContext));
assertEquals("Andy",e.getValue(eContext));
}
/*
* Testing the coercion of both the keys and the values to the correct type
*/
public void testSetGenericMapElementRequiresCoercion() throws Exception {
StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
Expression e = parse("mapOfStringToBoolean[42]");
assertNull(e.getValue(eContext));
// Key should be coerced to string representation of 42
e.setValue(eContext, "true");
// All keys should be strings
Set ks = parse("mapOfStringToBoolean.keySet()").getValue(eContext,Set.class);
for (Object o: ks) {
assertEquals(String.class,o.getClass());
}
// All values should be booleans
Collection vs = parse("mapOfStringToBoolean.values()").getValue(eContext,Collection.class);
for (Object o: vs) {
assertEquals(Boolean.class,o.getClass());
}
// One final test check coercion on the key for a map lookup
Object o = e.getValue(eContext);
assertEquals(Boolean.TRUE,o);
}
private Expression parse(String expressionString) throws Exception {
return parser.parseExpression(expressionString);
}
/**
* Call setValue() but expect it to fail.
*/
@ -167,7 +214,12 @@ public class SetValueTests extends ExpressionTestCase {
StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext();
assertTrue("Expression is not writeable but should be", e.isWritable(lContext));
e.setValue(lContext, value);
assertEquals("Retrieved value was not equal to set value", expectedValue, e.getValue(lContext));
Object a = expectedValue;
Object b = e.getValue(lContext);
if (!a.equals(b)) {
fail("Not the same: ["+a+"] type="+a.getClass()+" ["+b+"] type="+b.getClass());
// assertEquals("Retrieved value was not equal to set value", expectedValue, e.getValue(lContext));
}
} catch (EvaluationException ee) {
ee.printStackTrace();
fail("Unexpected Exception: " + ee.getMessage());

View File

@ -9,6 +9,7 @@ import java.util.Map;
@SuppressWarnings("unused")
public class Inventor {
private String name;
public String publicName;
private PlaceOfBirth placeOfBirth;
Date birthdate;
private int sinNumber;
@ -23,6 +24,8 @@ public class Inventor {
public boolean publicBoolean;
private boolean accessedThroughGetSet;
public List<Integer> listOfInteger = new ArrayList<Integer>();
public List<Boolean> booleanList = new ArrayList<Boolean>();
public Map<String,Boolean> mapOfStringToBoolean = new HashMap<String,Boolean>();
public Inventor(String name, Date birthdate, String nationality) {
this.name = name;
@ -37,6 +40,8 @@ public class Inventor {
testMap.put("friday", "freitag");
testMap.put("saturday", "samstag");
testMap.put("sunday", "sonntag");
booleanList.add(false);
booleanList.add(false);
}
public void setPlaceOfBirth(PlaceOfBirth placeOfBirth2) {

View File

@ -21,4 +21,16 @@ public class PlaceOfBirth {
return i*2;
}
public boolean equals(Object o) {
if (!(o instanceof PlaceOfBirth)) {
return false;
}
PlaceOfBirth oPOB = (PlaceOfBirth)o;
return (city.equals(oPOB.city));
}
public int hashCode() {
return city.hashCode();
}
}