revised core conversion package for BeanWrapper/BeanFactory integration

This commit is contained in:
Juergen Hoeller 2009-08-09 00:52:41 +00:00
parent 45a0cadf8e
commit dd67900109
15 changed files with 92 additions and 129 deletions

View File

@ -20,24 +20,23 @@ import org.springframework.core.convert.TypeDescriptor;
/**
* A type converter can convert values between different types encountered
* during expression evaluation.
* during expression evaluation. This is an SPI for the expression parser;
* see {@link org.springframework.core.convert.ConversionService} for the
* primary user API to Spring's conversion facilities.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public interface TypeConverter {
// TODO replace this stuff with Keiths spring-binding conversion code
// TODO should ExpressionException be thrown for lost precision in the case of coercion?
// TODO could remove the methods where the target is Class and just keep the TypeDescriptor variants
/**
* Convert (may coerce) a value from one type to another, for example from a boolean to a string.
* @param value the value to be converted
* @param targetType the type that the value should be converted to if possible
* @return the converted value
* @throws EvaluationException if conversion is not possible
* Return true if the type converter can convert the specified type to the desired target type.
* @param sourceType the type to be converted from
* @param targetType the type to be converted to
* @return true if that conversion can be performed
*/
<T> T convertValue(Object value, Class<T> targetType) throws EvaluationException;
boolean canConvert(Class<?> sourceType, Class<?> targetType);
/**
* Convert (may coerce) a value from one type to another, for example from a boolean to a string.
@ -50,20 +49,4 @@ public interface TypeConverter {
*/
Object convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException;
/**
* Return true if the type converter can convert the specified type to the desired target type.
* @param sourceType the type to be converted from
* @param targetType the type to be converted to
* @return true if that conversion can be performed
*/
boolean canConvert(Class<?> sourceType, Class<?> targetType);
/**
* Return true if the type converter can convert the specified type to the desired target type.
* @param sourceType the type to be converted from
* @param typeDescriptor a type descriptor that supplies extra information about the requested result type
* @return true if that conversion can be performed
*/
boolean canConvert(Class<?> sourceType, TypeDescriptor typeDescriptor);
}

View File

@ -16,6 +16,7 @@
package org.springframework.expression.common;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.util.ClassUtils;
@ -47,7 +48,7 @@ public abstract class ExpressionUtils {
return (T) value;
}
if (context != null) {
return context.getTypeConverter().convertValue(value, targetType);
return (T) context.getTypeConverter().convertValue(value, TypeDescriptor.valueOf(targetType));
}
throw new EvaluationException("Cannot convert value '" + value + "' to type '" + targetType.getName() + "'");
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* 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.
@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel;
import org.springframework.core.convert.TypeDescriptor;
@ -74,7 +75,7 @@ public class SpelExpression implements Expression {
Class<?> resultType = result.getClass();
if (!expectedResultType.isAssignableFrom(resultType)) {
// Attempt conversion to the requested type, may throw an exception
result = context.getTypeConverter().convertValue(result, expectedResultType);
result = context.getTypeConverter().convertValue(result, TypeDescriptor.valueOf(expectedResultType));
}
}
return (T) result;
@ -98,8 +99,7 @@ public class SpelExpression implements Expression {
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
ExpressionState eState = new ExpressionState(context, configuration);
TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
return typeDescriptor;
return ast.getValueInternal(eState).getTypeDescriptor();
}
public String getExpressionString() {

View File

@ -1,37 +0,0 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.springframework.core.convert.TypeDescriptor;
/**
* @author Andy Clement
* @since 3.0
*/
public interface CommonTypeDescriptors {
// need a better home for these - TypeDescriptor?
static TypeDescriptor<Boolean> BOOLEAN_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Boolean.class);
static TypeDescriptor<Integer> INTEGER_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Integer.class);
static TypeDescriptor<Character> CHARACTER_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Character.class);
static TypeDescriptor<Long> LONG_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Long.class);
static TypeDescriptor<Short> SHORT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Short.class);
static TypeDescriptor<Byte> BYTE_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Byte.class);
static TypeDescriptor<Float> FLOAT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Float.class);
static TypeDescriptor<Double> DOUBLE_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Double.class);
static TypeDescriptor<String> STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
static TypeDescriptor<Class> CLASS_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Class.class);
static TypeDescriptor<Object> OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);
}

View File

@ -55,7 +55,7 @@ public class Indexer extends SpelNodeImpl {
if (targetObject instanceof Map && (children[0] instanceof PropertyOrFieldReference)) {
PropertyOrFieldReference reference = (PropertyOrFieldReference)children[0];
index = reference.getName();
indexValue = new TypedValue(index,CommonTypeDescriptors.STRING_TYPE_DESCRIPTOR);
indexValue = new TypedValue(index, TypeDescriptor.valueOf(String.class));
} else {
// In case the map key is unqualified, we want it evaluated against the root object so
// temporarily push that on whilst evaluating the key
@ -85,7 +85,7 @@ public class Indexer extends SpelNodeImpl {
}
Object o = ((Map<?, ?>) targetObject).get(possiblyConvertedKey);
TypeDescriptor resultDescriptor = targetObjectTypeDescriptor.isMapEntryTypeKnown()?
TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapValueType()):CommonTypeDescriptors.OBJECT_TYPE_DESCRIPTOR;
TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapValueType()): TypeDescriptor.valueOf(Object.class);
return new TypedValue(o,resultDescriptor);
}

View File

@ -24,6 +24,7 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.core.convert.TypeDescriptor;
/**
* The common supertype of all AST nodes in a parsed Spring Expression Language format expression.
@ -31,8 +32,20 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
* @author Andy Clement
* @since 3.0
*/
public abstract class SpelNodeImpl implements SpelNode, CommonTypeDescriptors {
public abstract class SpelNodeImpl implements SpelNode {
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);
private static SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0];
protected int pos; // start = top 16bits, end = bottom 16bits

View File

@ -13,10 +13,11 @@
* 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.CommonTypeDescriptors;
import org.springframework.core.convert.TypeDescriptor;
/**
* @author Andy Clement
@ -28,7 +29,7 @@ public class BooleanTypedValue extends TypedValue {
public static final BooleanTypedValue False = new BooleanTypedValue(false);
private BooleanTypedValue(boolean b) {
super(b,CommonTypeDescriptors.BOOLEAN_TYPE_DESCRIPTOR);
super(b, TypeDescriptor.valueOf(Boolean.class));
}
public static BooleanTypedValue forValue(boolean b) {

View File

@ -20,6 +20,7 @@ import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.SpelEvaluationException;
@ -228,7 +229,7 @@ public class ReflectionHelper {
} else {
targetType = requiredParameterTypes[argPosition];
}
arguments[argPosition] = converter.convertValue(arguments[argPosition], targetType);
arguments[argPosition] = converter.convertValue(arguments[argPosition], TypeDescriptor.valueOf(targetType));
}
}
@ -267,7 +268,7 @@ public class ReflectionHelper {
if (converter == null) {
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, arguments[i].getClass().getName(),targetType);
}
arguments[i] = converter.convertValue(arguments[i], targetType);
arguments[i] = converter.convertValue(arguments[i], TypeDescriptor.valueOf(targetType));
}
} catch (EvaluationException ex) {
// allows for another type converter throwing a different kind of EvaluationException

View File

@ -23,6 +23,7 @@ import org.springframework.expression.AccessException;
import org.springframework.expression.ConstructorExecutor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.TypedValue;
import org.springframework.util.ReflectionUtils;
/**
* A simple ConstructorExecutor implementation that runs a constructor using reflective invocation.
@ -32,33 +33,35 @@ import org.springframework.expression.TypedValue;
*/
class ReflectiveConstructorExecutor implements ConstructorExecutor {
private final Constructor<?> c;
private final Constructor<?> ctor;
// When the constructor was found, we will have determined if arguments need to be converted for it
// to be invoked. Conversion won't be cheap so let's only do it if necessary.
private final int[] argsRequiringConversion;
public ReflectiveConstructorExecutor(Constructor<?> constructor, int[] argsRequiringConversion) {
c = constructor;
public ReflectiveConstructorExecutor(Constructor<?> ctor, int[] argsRequiringConversion) {
this.ctor = ctor;
this.argsRequiringConversion = argsRequiringConversion;
}
public TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException {
try {
if (argsRequiringConversion != null && arguments != null) {
ReflectionHelper.convertArguments(c.getParameterTypes(), c.isVarArgs(),
context.getTypeConverter(), argsRequiringConversion, arguments);
ReflectionHelper.convertArguments(this.ctor.getParameterTypes(),
this.ctor.isVarArgs(), context.getTypeConverter(),
this.argsRequiringConversion, arguments);
}
if (c.isVarArgs()) {
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(c.getParameterTypes(), arguments);
if (this.ctor.isVarArgs()) {
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(
this.ctor.getParameterTypes(), arguments);
}
if (!c.isAccessible()) {
c.setAccessible(true);
}
return new TypedValue(c.newInstance(arguments),TypeDescriptor.valueOf(c.getClass()));
} catch (Exception ex) {
throw new AccessException("Problem invoking constructor: " + c, ex);
ReflectionUtils.makeAccessible(this.ctor);
return new TypedValue(this.ctor.newInstance(arguments),
TypeDescriptor.valueOf(this.ctor.getClass()));
}
catch (Exception ex) {
throw new AccessException("Problem invoking constructor: " + this.ctor, ex);
}
}

View File

@ -16,10 +16,11 @@
package org.springframework.expression.spel.support;
import org.springframework.core.convert.ConvertException;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultTypeConverter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.SpelEvaluationException;
@ -27,29 +28,33 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.Assert;
/**
* Default implementation of the {@link TypeConverter} interface,
* delegating to a core Spring {@link ConversionService}.
*
* @author Juergen Hoeller
* @author Andy Clement
* @since 3.0
* @see org.springframework.core.convert.ConversionService
*/
public class StandardTypeConverter implements TypeConverter {
private org.springframework.core.convert.TypeConverter typeConverter;
private final ConversionService typeConverter;
public StandardTypeConverter() {
this.typeConverter = new DefaultTypeConverter();
this.typeConverter = new DefaultConversionService();
}
public StandardTypeConverter(org.springframework.core.convert.TypeConverter typeConverter) {
Assert.notNull(typeConverter, "TypeConverter must not be null");
public StandardTypeConverter(ConversionService typeConverter) {
Assert.notNull(typeConverter, "ConversionService must not be null");
this.typeConverter = typeConverter;
}
@SuppressWarnings("unchecked")
public <T> T convertValue(Object value, Class<T> targetType) throws EvaluationException {
return (T) convertValue(value, TypeDescriptor.valueOf(targetType));
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
return this.typeConverter.canConvert(sourceType, targetType);
}
@SuppressWarnings("unchecked")
public Object convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException {
try {
return this.typeConverter.convert(value, typeDescriptor);
@ -57,17 +62,9 @@ public class StandardTypeConverter implements TypeConverter {
catch (ConverterNotFoundException cenfe) {
throw new SpelEvaluationException(cenfe, SpelMessage.TYPE_CONVERSION_ERROR, value.getClass(), typeDescriptor.asString());
}
catch (ConvertException ce) {
catch (ConversionException ce) {
throw new SpelEvaluationException(ce, SpelMessage.TYPE_CONVERSION_ERROR, value.getClass(), typeDescriptor.asString());
}
}
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
return canConvert(sourceType, TypeDescriptor.valueOf(targetType));
}
public boolean canConvert(Class<?> sourceType, TypeDescriptor targetType) {
return this.typeConverter.canConvert(sourceType, targetType);
}
}

View File

@ -24,15 +24,14 @@ import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultTypeConverter;
import org.springframework.core.convert.support.GenericTypeConverter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* Expression evaluation where the TypeConverter plugged in is the {@link GenericTypeConverter}
* Expression evaluation where the TypeConverter plugged in is the {@link org.springframework.core.convert.support.GenericConversionService}
*
* @author Andy Clement
*/
@ -99,7 +98,7 @@ public class ExpressionTestsUsingCoreConversionService extends ExpressionTestCas
*/
private static class TypeConvertorUsingConversionService implements TypeConverter {
private final DefaultTypeConverter service = new DefaultTypeConverter();
private final DefaultConversionService service = new DefaultConversionService();
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
return this.service.canConvert(sourceType, TypeDescriptor.valueOf(targetType));

View File

@ -27,9 +27,9 @@ import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ast.CommonTypeDescriptors;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.core.convert.TypeDescriptor;
/**
* Testing variations on map access.
@ -78,7 +78,7 @@ public class MapAccessTests extends ExpressionTestCase {
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((Map) target).get(name),CommonTypeDescriptors.OBJECT_TYPE_DESCRIPTOR);
return new TypedValue(((Map) target).get(name), TypeDescriptor.valueOf(Object.class));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {

View File

@ -17,15 +17,15 @@
package org.springframework.expression.spel;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ast.CommonTypeDescriptors;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@ -154,7 +154,7 @@ public class PropertyAccessTests extends ExpressionTestCase {
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
if (!name.equals("flibbles"))
throw new RuntimeException("Assertion Failed! name should be flibbles");
return new TypedValue(flibbles,CommonTypeDescriptors.STRING_TYPE_DESCRIPTOR);
return new TypedValue(flibbles, TypeDescriptor.valueOf(String.class));
}
public void write(EvaluationContext context, Object target, String name, Object newValue)
@ -162,8 +162,8 @@ public class PropertyAccessTests extends ExpressionTestCase {
if (!name.equals("flibbles"))
throw new RuntimeException("Assertion Failed! name should be flibbles");
try {
flibbles = context.getTypeConverter().convertValue(newValue, Integer.class);
} catch (EvaluationException e) {
flibbles = (Integer) context.getTypeConverter().convertValue(newValue, TypeDescriptor.valueOf(Integer.class));
}catch (EvaluationException e) {
throw new AccessException("Cannot set flibbles to an object of type '" + newValue.getClass() + "'");
}
}

View File

@ -21,8 +21,9 @@ import java.util.Map;
import java.util.Properties;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
@ -30,7 +31,6 @@ import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ast.CommonTypeDescriptors;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.ReflectivePropertyResolver;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@ -219,7 +219,7 @@ public class SpringEL300Tests extends ExpressionTestCase {
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((Map) target).get(name),CommonTypeDescriptors.OBJECT_TYPE_DESCRIPTOR);
return new TypedValue(((Map) target).get(name), TypeDescriptor.valueOf(Object.class));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {

View File

@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.support;
package org.springframework.expression.spel.support;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.core.convert.support.DefaultTypeConverter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.OperatorOverloader;
@ -70,10 +73,9 @@ public class StandardComponentsTests {
@Test
public void testStandardTypeConverter() throws EvaluationException {
TypeConverter tc = new StandardTypeConverter(new DefaultTypeConverter());
tc.convertValue(3,Double.class);
TypeConverter tc = new StandardTypeConverter(new DefaultConversionService());
tc.convertValue(3, TypeDescriptor.valueOf(Double.class));
}
}