propagate full TypeDescriptor for field-level conversion as well

This commit is contained in:
Juergen Hoeller 2009-11-27 01:43:14 +00:00
parent 973fb6ce40
commit ac490114ae
4 changed files with 78 additions and 39 deletions

View File

@ -42,6 +42,15 @@ class BeanTypeDescriptor extends TypeDescriptor {
private Annotation[] cachedAnnotations; private Annotation[] cachedAnnotations;
/**
* Create a new BeanTypeDescriptor for the given bean property.
* @param propertyDescriptor the corresponding JavaBean PropertyDescriptor
*/
public BeanTypeDescriptor(PropertyDescriptor propertyDescriptor) {
super(BeanUtils.getWriteMethodParameter(propertyDescriptor));
this.propertyDescriptor = propertyDescriptor;
}
/** /**
* Create a new BeanTypeDescriptor for the given bean property. * Create a new BeanTypeDescriptor for the given bean property.
* @param methodParameter the target method parameter * @param methodParameter the target method parameter
@ -53,6 +62,13 @@ class BeanTypeDescriptor extends TypeDescriptor {
} }
/**
* Return the underlying PropertyDescriptor.
*/
public PropertyDescriptor getPropertyDescriptor() {
return this.propertyDescriptor;
}
@Override @Override
public Annotation[] getAnnotations() { public Annotation[] getAnnotations() {
Annotation[] anns = this.cachedAnnotations; Annotation[] anns = this.cachedAnnotations;

View File

@ -37,6 +37,7 @@ import java.util.Set;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.core.CollectionFactory; import org.springframework.core.CollectionFactory;
import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
@ -969,7 +970,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
// Pass full property name and old value in here, since we want full // Pass full property name and old value in here, since we want full
// conversion ability for map values. // conversion ability for map values.
convertedMapValue = this.typeConverterDelegate.convertIfNecessary( convertedMapValue = this.typeConverterDelegate.convertIfNecessary(
propertyName, oldValue, pv.getValue(), mapValueType, null, propertyName, oldValue, pv.getValue(), mapValueType,
new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length + 1)); new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length + 1));
} }
catch (IllegalArgumentException ex) { catch (IllegalArgumentException ex) {

View File

@ -122,8 +122,7 @@ public class DirectFieldAccessor extends AbstractPropertyAccessor {
try { try {
ReflectionUtils.makeAccessible(field); ReflectionUtils.makeAccessible(field);
oldValue = field.get(this.target); oldValue = field.get(this.target);
Object convertedValue = Object convertedValue = this.typeConverterDelegate.convertIfNecessary(oldValue, newValue, field);
this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, field.getType());
field.set(this.target, convertedValue); field.set(this.target, convertedValue);
} }
catch (IllegalAccessException ex) { catch (IllegalAccessException ex) {
@ -139,8 +138,8 @@ public class DirectFieldAccessor extends AbstractPropertyAccessor {
} }
} }
public Object convertIfNecessary( public <T> T convertIfNecessary(
Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException { Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
try { try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam); return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
} }

View File

@ -85,7 +85,7 @@ class TypeConverterDelegate {
* @throws IllegalArgumentException if type conversion failed * @throws IllegalArgumentException if type conversion failed
*/ */
public <T> T convertIfNecessary(Object newValue, Class<T> requiredType) throws IllegalArgumentException { public <T> T convertIfNecessary(Object newValue, Class<T> requiredType) throws IllegalArgumentException {
return convertIfNecessary(null, null, newValue, requiredType, null, null); return convertIfNecessary(null, null, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
} }
/** /**
@ -101,7 +101,8 @@ class TypeConverterDelegate {
public <T> T convertIfNecessary(Object newValue, Class<T> requiredType, MethodParameter methodParam) public <T> T convertIfNecessary(Object newValue, Class<T> requiredType, MethodParameter methodParam)
throws IllegalArgumentException { throws IllegalArgumentException {
return convertIfNecessary(null, null, newValue, requiredType, null, methodParam); return convertIfNecessary(null, null, newValue, requiredType,
(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
} }
/** /**
@ -118,7 +119,25 @@ class TypeConverterDelegate {
String propertyName, Object oldValue, Object newValue, Class<T> requiredType) String propertyName, Object oldValue, Object newValue, Class<T> requiredType)
throws IllegalArgumentException { throws IllegalArgumentException {
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, null, null); return convertIfNecessary(propertyName, oldValue, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
}
/**
* Convert the value to the required type (if necessary from a String),
* for the specified property.
* @param propertyName name of the property
* @param oldValue the previous value, if available (may be <code>null</code>)
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or <code>null</code> if not known, for example in case of a collection element)
* @param methodParam the method parameter that is the target of the conversion
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class<T> requiredType, MethodParameter methodParam) throws IllegalArgumentException {
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, new TypeDescriptor(methodParam));
} }
/** /**
@ -132,9 +151,22 @@ class TypeConverterDelegate {
public Object convertIfNecessary(Object oldValue, Object newValue, PropertyDescriptor descriptor) public Object convertIfNecessary(Object oldValue, Object newValue, PropertyDescriptor descriptor)
throws IllegalArgumentException { throws IllegalArgumentException {
return convertIfNecessary( return convertIfNecessary(descriptor.getName(), oldValue, newValue, descriptor.getPropertyType(),
descriptor.getName(), oldValue, newValue, descriptor.getPropertyType(), descriptor, new BeanTypeDescriptor(descriptor));
BeanUtils.getWriteMethodParameter(descriptor)); }
/**
* Convert the value to the required type for the specified property.
* @param oldValue the previous value, if available (may be <code>null</code>)
* @param newValue the proposed new value
* @param field the field that is the target of the conversion
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public Object convertIfNecessary(Object oldValue, Object newValue, Field field)
throws IllegalArgumentException {
return convertIfNecessary(field.getName(), oldValue, newValue, field.getType(), new TypeDescriptor(field));
} }
@ -146,17 +178,13 @@ class TypeConverterDelegate {
* @param newValue the proposed new value * @param newValue the proposed new value
* @param requiredType the type we must convert to * @param requiredType the type we must convert to
* (or <code>null</code> if not known, for example in case of a collection element) * (or <code>null</code> if not known, for example in case of a collection element)
* @param descriptor the JavaBeans descriptor for the property * @param typeDescriptor the descriptor for the target property or field
* @param methodParam the method parameter that is the target of the conversion
* (may be <code>null</code>)
* @return the new value, possibly the result of type conversion * @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed * @throws IllegalArgumentException if type conversion failed
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <T> T convertIfNecessary( private <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
String propertyName, Object oldValue, Object newValue, Class<T> requiredType, Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
PropertyDescriptor descriptor, MethodParameter methodParam)
throws IllegalArgumentException {
Object convertedValue = newValue; Object convertedValue = newValue;
@ -167,23 +195,15 @@ class TypeConverterDelegate {
ConversionService conversionService = this.propertyEditorRegistry.getConversionService(); ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && convertedValue != null) { if (editor == null && conversionService != null && convertedValue != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.valueOf(convertedValue.getClass()); TypeDescriptor sourceTypeDesc = TypeDescriptor.valueOf(convertedValue.getClass());
TypeDescriptor targetTypeDesc; if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
if (methodParam != null) { return (T) conversionService.convert(convertedValue, sourceTypeDesc, typeDescriptor);
targetTypeDesc = (descriptor != null ?
new BeanTypeDescriptor(methodParam, descriptor) : new TypeDescriptor(methodParam));
}
else {
targetTypeDesc = TypeDescriptor.valueOf(requiredType);
}
if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
} }
} }
// Value not of required type? // Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (editor == null) { if (editor == null) {
editor = findDefaultEditor(requiredType, descriptor); editor = findDefaultEditor(requiredType, typeDescriptor);
} }
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor); convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
} }
@ -199,12 +219,12 @@ class TypeConverterDelegate {
else if (convertedValue instanceof Collection) { else if (convertedValue instanceof Collection) {
// Convert elements to target type, if determined. // Convert elements to target type, if determined.
convertedValue = convertToTypedCollection( convertedValue = convertToTypedCollection(
(Collection) convertedValue, propertyName, requiredType, methodParam); (Collection) convertedValue, propertyName, requiredType, typeDescriptor);
} }
else if (convertedValue instanceof Map) { else if (convertedValue instanceof Map) {
// Convert keys and values to respective target type, if determined. // Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap( convertedValue = convertToTypedMap(
(Map) convertedValue, propertyName, requiredType, methodParam); (Map) convertedValue, propertyName, requiredType, typeDescriptor);
} }
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) { if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0); convertedValue = Array.get(convertedValue, 0);
@ -316,10 +336,11 @@ class TypeConverterDelegate {
* @param descriptor the JavaBeans descriptor for the property * @param descriptor the JavaBeans descriptor for the property
* @return the corresponding editor, or <code>null</code> if none * @return the corresponding editor, or <code>null</code> if none
*/ */
protected PropertyEditor findDefaultEditor(Class requiredType, PropertyDescriptor descriptor) { protected PropertyEditor findDefaultEditor(Class requiredType, TypeDescriptor typeDescriptor) {
PropertyEditor editor = null; PropertyEditor editor = null;
if (descriptor != null) { if (typeDescriptor instanceof BeanTypeDescriptor) {
editor = descriptor.createPropertyEditor(this.targetObject); PropertyDescriptor pd = ((BeanTypeDescriptor) typeDescriptor).getPropertyDescriptor();
editor = pd.createPropertyEditor(this.targetObject);
} }
if (editor == null && requiredType != null) { if (editor == null && requiredType != null) {
// No custom editor -> check BeanWrapperImpl's default editors. // No custom editor -> check BeanWrapperImpl's default editors.
@ -484,9 +505,10 @@ class TypeConverterDelegate {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Collection convertToTypedCollection( protected Collection convertToTypedCollection(
Collection original, String propertyName, Class requiredType, MethodParameter methodParam) { Collection original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) {
boolean originalAllowed = requiredType.isInstance(original); boolean originalAllowed = requiredType.isInstance(original);
MethodParameter methodParam = typeDescriptor.getMethodParameter();
Class elementType = null; Class elementType = null;
if (methodParam != null) { if (methodParam != null) {
elementType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam); elementType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
@ -540,7 +562,7 @@ class TypeConverterDelegate {
methodParam.increaseNestingLevel(); methodParam.increaseNestingLevel();
} }
Object convertedElement = Object convertedElement =
convertIfNecessary(indexedPropertyName, null, element, elementType, null, methodParam); convertIfNecessary(indexedPropertyName, null, element, elementType, typeDescriptor);
if (methodParam != null) { if (methodParam != null) {
methodParam.decreaseNestingLevel(); methodParam.decreaseNestingLevel();
} }
@ -551,10 +573,11 @@ class TypeConverterDelegate {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Map convertToTypedMap(Map original, String propertyName, Class requiredType, MethodParameter methodParam) { protected Map convertToTypedMap(Map original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) {
boolean originalAllowed = requiredType.isInstance(original); boolean originalAllowed = requiredType.isInstance(original);
Class keyType = null; Class keyType = null;
Class valueType = null; Class valueType = null;
MethodParameter methodParam = typeDescriptor.getMethodParameter();
if (methodParam != null) { if (methodParam != null) {
keyType = GenericCollectionTypeResolver.getMapKeyParameterType(methodParam); keyType = GenericCollectionTypeResolver.getMapKeyParameterType(methodParam);
valueType = GenericCollectionTypeResolver.getMapValueParameterType(methodParam); valueType = GenericCollectionTypeResolver.getMapValueParameterType(methodParam);
@ -609,11 +632,11 @@ class TypeConverterDelegate {
methodParam.increaseNestingLevel(); methodParam.increaseNestingLevel();
methodParam.setTypeIndexForCurrentLevel(0); methodParam.setTypeIndexForCurrentLevel(0);
} }
Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType, null, methodParam); Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType, typeDescriptor);
if (methodParam != null) { if (methodParam != null) {
methodParam.setTypeIndexForCurrentLevel(1); methodParam.setTypeIndexForCurrentLevel(1);
} }
Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType, null, methodParam); Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType, typeDescriptor);
if (methodParam != null) { if (methodParam != null) {
methodParam.decreaseNestingLevel(); methodParam.decreaseNestingLevel();
} }