propagate full TypeDescriptor for field-level conversion as well
This commit is contained in:
parent
973fb6ce40
commit
ac490114ae
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue