diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/BeanTypeDescriptor.java b/org.springframework.beans/src/main/java/org/springframework/beans/BeanTypeDescriptor.java index 06742155f5e..b9099fe42f5 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/BeanTypeDescriptor.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/BeanTypeDescriptor.java @@ -42,6 +42,15 @@ class BeanTypeDescriptor extends TypeDescriptor { 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. * @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 public Annotation[] getAnnotations() { Annotation[] anns = this.cachedAnnotations; diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index 356dc09f253..5b1b6a01e24 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -37,6 +37,7 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.core.CollectionFactory; import org.springframework.core.GenericCollectionTypeResolver; 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 // conversion ability for map values. convertedMapValue = this.typeConverterDelegate.convertIfNecessary( - propertyName, oldValue, pv.getValue(), mapValueType, null, + propertyName, oldValue, pv.getValue(), mapValueType, new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length + 1)); } catch (IllegalArgumentException ex) { diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java b/org.springframework.beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java index 760b6f7fb0d..c2d6629023d 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java @@ -122,8 +122,7 @@ public class DirectFieldAccessor extends AbstractPropertyAccessor { try { ReflectionUtils.makeAccessible(field); oldValue = field.get(this.target); - Object convertedValue = - this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, field.getType()); + Object convertedValue = this.typeConverterDelegate.convertIfNecessary(oldValue, newValue, field); field.set(this.target, convertedValue); } catch (IllegalAccessException ex) { @@ -139,8 +138,8 @@ public class DirectFieldAccessor extends AbstractPropertyAccessor { } } - public Object convertIfNecessary( - Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException { + public T convertIfNecessary( + Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException { try { return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam); } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index ced69a4a0e8..85100b836c2 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -85,7 +85,7 @@ class TypeConverterDelegate { * @throws IllegalArgumentException if type conversion failed */ public T convertIfNecessary(Object newValue, Class 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 convertIfNecessary(Object newValue, Class requiredType, MethodParameter methodParam) 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 requiredType) 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 null) + * @param newValue the proposed new value + * @param requiredType the type we must convert to + * (or null 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 convertIfNecessary(String propertyName, Object oldValue, Object newValue, + Class 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) throws IllegalArgumentException { - return convertIfNecessary( - descriptor.getName(), oldValue, newValue, descriptor.getPropertyType(), descriptor, - BeanUtils.getWriteMethodParameter(descriptor)); + return convertIfNecessary(descriptor.getName(), oldValue, newValue, descriptor.getPropertyType(), + new BeanTypeDescriptor(descriptor)); + } + + /** + * Convert the value to the required type for the specified property. + * @param oldValue the previous value, if available (may be null) + * @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 requiredType the type we must convert to * (or null if not known, for example in case of a collection element) - * @param descriptor the JavaBeans descriptor for the property - * @param methodParam the method parameter that is the target of the conversion - * (may be null) + * @param typeDescriptor the descriptor for the target property or field * @return the new value, possibly the result of type conversion * @throws IllegalArgumentException if type conversion failed */ @SuppressWarnings("unchecked") - protected T convertIfNecessary( - String propertyName, Object oldValue, Object newValue, Class requiredType, - PropertyDescriptor descriptor, MethodParameter methodParam) - throws IllegalArgumentException { + private T convertIfNecessary(String propertyName, Object oldValue, Object newValue, + Class requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException { Object convertedValue = newValue; @@ -167,23 +195,15 @@ class TypeConverterDelegate { ConversionService conversionService = this.propertyEditorRegistry.getConversionService(); if (editor == null && conversionService != null && convertedValue != null) { TypeDescriptor sourceTypeDesc = TypeDescriptor.valueOf(convertedValue.getClass()); - TypeDescriptor targetTypeDesc; - if (methodParam != null) { - 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); + if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) { + return (T) conversionService.convert(convertedValue, sourceTypeDesc, typeDescriptor); } } // Value not of required type? if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { if (editor == null) { - editor = findDefaultEditor(requiredType, descriptor); + editor = findDefaultEditor(requiredType, typeDescriptor); } convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor); } @@ -199,12 +219,12 @@ class TypeConverterDelegate { else if (convertedValue instanceof Collection) { // Convert elements to target type, if determined. convertedValue = convertToTypedCollection( - (Collection) convertedValue, propertyName, requiredType, methodParam); + (Collection) convertedValue, propertyName, requiredType, typeDescriptor); } else if (convertedValue instanceof Map) { // Convert keys and values to respective target type, if determined. convertedValue = convertToTypedMap( - (Map) convertedValue, propertyName, requiredType, methodParam); + (Map) convertedValue, propertyName, requiredType, typeDescriptor); } if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) { convertedValue = Array.get(convertedValue, 0); @@ -316,10 +336,11 @@ class TypeConverterDelegate { * @param descriptor the JavaBeans descriptor for the property * @return the corresponding editor, or null if none */ - protected PropertyEditor findDefaultEditor(Class requiredType, PropertyDescriptor descriptor) { + protected PropertyEditor findDefaultEditor(Class requiredType, TypeDescriptor typeDescriptor) { PropertyEditor editor = null; - if (descriptor != null) { - editor = descriptor.createPropertyEditor(this.targetObject); + if (typeDescriptor instanceof BeanTypeDescriptor) { + PropertyDescriptor pd = ((BeanTypeDescriptor) typeDescriptor).getPropertyDescriptor(); + editor = pd.createPropertyEditor(this.targetObject); } if (editor == null && requiredType != null) { // No custom editor -> check BeanWrapperImpl's default editors. @@ -484,9 +505,10 @@ class TypeConverterDelegate { @SuppressWarnings("unchecked") protected Collection convertToTypedCollection( - Collection original, String propertyName, Class requiredType, MethodParameter methodParam) { + Collection original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) { boolean originalAllowed = requiredType.isInstance(original); + MethodParameter methodParam = typeDescriptor.getMethodParameter(); Class elementType = null; if (methodParam != null) { elementType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam); @@ -540,7 +562,7 @@ class TypeConverterDelegate { methodParam.increaseNestingLevel(); } Object convertedElement = - convertIfNecessary(indexedPropertyName, null, element, elementType, null, methodParam); + convertIfNecessary(indexedPropertyName, null, element, elementType, typeDescriptor); if (methodParam != null) { methodParam.decreaseNestingLevel(); } @@ -551,10 +573,11 @@ class TypeConverterDelegate { } @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); Class keyType = null; Class valueType = null; + MethodParameter methodParam = typeDescriptor.getMethodParameter(); if (methodParam != null) { keyType = GenericCollectionTypeResolver.getMapKeyParameterType(methodParam); valueType = GenericCollectionTypeResolver.getMapValueParameterType(methodParam); @@ -609,11 +632,11 @@ class TypeConverterDelegate { methodParam.increaseNestingLevel(); methodParam.setTypeIndexForCurrentLevel(0); } - Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType, null, methodParam); + Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType, typeDescriptor); if (methodParam != null) { methodParam.setTypeIndexForCurrentLevel(1); } - Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType, null, methodParam); + Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType, typeDescriptor); if (methodParam != null) { methodParam.decreaseNestingLevel(); }