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;
|
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;
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue