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;
/**
* 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;

View File

@ -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) {

View File

@ -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> T convertIfNecessary(
Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}

View File

@ -85,7 +85,7 @@ class TypeConverterDelegate {
* @throws IllegalArgumentException if type conversion failed
*/
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)
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)
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)
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 <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 requiredType the type we must convert to
* (or <code>null</code> 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 <code>null</code>)
* @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> T convertIfNecessary(
String propertyName, Object oldValue, Object newValue, Class<T> requiredType,
PropertyDescriptor descriptor, MethodParameter methodParam)
throws IllegalArgumentException {
private <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class<T> 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 <code>null</code> 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();
}