Skip to content

Commit ac49011

Browse files
committed
propagate full TypeDescriptor for field-level conversion as well
1 parent 973fb6c commit ac49011

File tree

4 files changed

+78
-39
lines changed

4 files changed

+78
-39
lines changed

org.springframework.beans/src/main/java/org/springframework/beans/BeanTypeDescriptor.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ class BeanTypeDescriptor extends TypeDescriptor {
4242
private Annotation[] cachedAnnotations;
4343

4444

45+
/**
46+
* Create a new BeanTypeDescriptor for the given bean property.
47+
* @param propertyDescriptor the corresponding JavaBean PropertyDescriptor
48+
*/
49+
public BeanTypeDescriptor(PropertyDescriptor propertyDescriptor) {
50+
super(BeanUtils.getWriteMethodParameter(propertyDescriptor));
51+
this.propertyDescriptor = propertyDescriptor;
52+
}
53+
4554
/**
4655
* Create a new BeanTypeDescriptor for the given bean property.
4756
* @param methodParameter the target method parameter
@@ -53,6 +62,13 @@ public BeanTypeDescriptor(MethodParameter methodParameter, PropertyDescriptor pr
5362
}
5463

5564

65+
/**
66+
* Return the underlying PropertyDescriptor.
67+
*/
68+
public PropertyDescriptor getPropertyDescriptor() {
69+
return this.propertyDescriptor;
70+
}
71+
5672
@Override
5773
public Annotation[] getAnnotations() {
5874
Annotation[] anns = this.cachedAnnotations;

org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
import org.apache.commons.logging.Log;
3939
import org.apache.commons.logging.LogFactory;
40+
4041
import org.springframework.core.CollectionFactory;
4142
import org.springframework.core.GenericCollectionTypeResolver;
4243
import org.springframework.core.MethodParameter;
@@ -969,7 +970,7 @@ else if (propValue instanceof Map) {
969970
// Pass full property name and old value in here, since we want full
970971
// conversion ability for map values.
971972
convertedMapValue = this.typeConverterDelegate.convertIfNecessary(
972-
propertyName, oldValue, pv.getValue(), mapValueType, null,
973+
propertyName, oldValue, pv.getValue(), mapValueType,
973974
new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length + 1));
974975
}
975976
catch (IllegalArgumentException ex) {

org.springframework.beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,7 @@ public void setPropertyValue(String propertyName, Object newValue) throws BeansE
122122
try {
123123
ReflectionUtils.makeAccessible(field);
124124
oldValue = field.get(this.target);
125-
Object convertedValue =
126-
this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, field.getType());
125+
Object convertedValue = this.typeConverterDelegate.convertIfNecessary(oldValue, newValue, field);
127126
field.set(this.target, convertedValue);
128127
}
129128
catch (IllegalAccessException ex) {
@@ -139,8 +138,8 @@ public void setPropertyValue(String propertyName, Object newValue) throws BeansE
139138
}
140139
}
141140

142-
public Object convertIfNecessary(
143-
Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException {
141+
public <T> T convertIfNecessary(
142+
Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
144143
try {
145144
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
146145
}

org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistr
8585
* @throws IllegalArgumentException if type conversion failed
8686
*/
8787
public <T> T convertIfNecessary(Object newValue, Class<T> requiredType) throws IllegalArgumentException {
88-
return convertIfNecessary(null, null, newValue, requiredType, null, null);
88+
return convertIfNecessary(null, null, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
8989
}
9090

9191
/**
@@ -101,7 +101,8 @@ public <T> T convertIfNecessary(Object newValue, Class<T> requiredType) throws I
101101
public <T> T convertIfNecessary(Object newValue, Class<T> requiredType, MethodParameter methodParam)
102102
throws IllegalArgumentException {
103103

104-
return convertIfNecessary(null, null, newValue, requiredType, null, methodParam);
104+
return convertIfNecessary(null, null, newValue, requiredType,
105+
(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
105106
}
106107

107108
/**
@@ -118,7 +119,25 @@ public <T> T convertIfNecessary(
118119
String propertyName, Object oldValue, Object newValue, Class<T> requiredType)
119120
throws IllegalArgumentException {
120121

121-
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, null, null);
122+
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
123+
}
124+
125+
/**
126+
* Convert the value to the required type (if necessary from a String),
127+
* for the specified property.
128+
* @param propertyName name of the property
129+
* @param oldValue the previous value, if available (may be <code>null</code>)
130+
* @param newValue the proposed new value
131+
* @param requiredType the type we must convert to
132+
* (or <code>null</code> if not known, for example in case of a collection element)
133+
* @param methodParam the method parameter that is the target of the conversion
134+
* @return the new value, possibly the result of type conversion
135+
* @throws IllegalArgumentException if type conversion failed
136+
*/
137+
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
138+
Class<T> requiredType, MethodParameter methodParam) throws IllegalArgumentException {
139+
140+
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, new TypeDescriptor(methodParam));
122141
}
123142

124143
/**
@@ -132,9 +151,22 @@ public <T> T convertIfNecessary(
132151
public Object convertIfNecessary(Object oldValue, Object newValue, PropertyDescriptor descriptor)
133152
throws IllegalArgumentException {
134153

135-
return convertIfNecessary(
136-
descriptor.getName(), oldValue, newValue, descriptor.getPropertyType(), descriptor,
137-
BeanUtils.getWriteMethodParameter(descriptor));
154+
return convertIfNecessary(descriptor.getName(), oldValue, newValue, descriptor.getPropertyType(),
155+
new BeanTypeDescriptor(descriptor));
156+
}
157+
158+
/**
159+
* Convert the value to the required type for the specified property.
160+
* @param oldValue the previous value, if available (may be <code>null</code>)
161+
* @param newValue the proposed new value
162+
* @param field the field that is the target of the conversion
163+
* @return the new value, possibly the result of type conversion
164+
* @throws IllegalArgumentException if type conversion failed
165+
*/
166+
public Object convertIfNecessary(Object oldValue, Object newValue, Field field)
167+
throws IllegalArgumentException {
168+
169+
return convertIfNecessary(field.getName(), oldValue, newValue, field.getType(), new TypeDescriptor(field));
138170
}
139171

140172

@@ -146,17 +178,13 @@ public Object convertIfNecessary(Object oldValue, Object newValue, PropertyDescr
146178
* @param newValue the proposed new value
147179
* @param requiredType the type we must convert to
148180
* (or <code>null</code> if not known, for example in case of a collection element)
149-
* @param descriptor the JavaBeans descriptor for the property
150-
* @param methodParam the method parameter that is the target of the conversion
151-
* (may be <code>null</code>)
181+
* @param typeDescriptor the descriptor for the target property or field
152182
* @return the new value, possibly the result of type conversion
153183
* @throws IllegalArgumentException if type conversion failed
154184
*/
155185
@SuppressWarnings("unchecked")
156-
protected <T> T convertIfNecessary(
157-
String propertyName, Object oldValue, Object newValue, Class<T> requiredType,
158-
PropertyDescriptor descriptor, MethodParameter methodParam)
159-
throws IllegalArgumentException {
186+
private <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
187+
Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
160188

161189
Object convertedValue = newValue;
162190

@@ -167,23 +195,15 @@ protected <T> T convertIfNecessary(
167195
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
168196
if (editor == null && conversionService != null && convertedValue != null) {
169197
TypeDescriptor sourceTypeDesc = TypeDescriptor.valueOf(convertedValue.getClass());
170-
TypeDescriptor targetTypeDesc;
171-
if (methodParam != null) {
172-
targetTypeDesc = (descriptor != null ?
173-
new BeanTypeDescriptor(methodParam, descriptor) : new TypeDescriptor(methodParam));
174-
}
175-
else {
176-
targetTypeDesc = TypeDescriptor.valueOf(requiredType);
177-
}
178-
if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
179-
return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
198+
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
199+
return (T) conversionService.convert(convertedValue, sourceTypeDesc, typeDescriptor);
180200
}
181201
}
182202

183203
// Value not of required type?
184204
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
185205
if (editor == null) {
186-
editor = findDefaultEditor(requiredType, descriptor);
206+
editor = findDefaultEditor(requiredType, typeDescriptor);
187207
}
188208
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
189209
}
@@ -199,12 +219,12 @@ protected <T> T convertIfNecessary(
199219
else if (convertedValue instanceof Collection) {
200220
// Convert elements to target type, if determined.
201221
convertedValue = convertToTypedCollection(
202-
(Collection) convertedValue, propertyName, requiredType, methodParam);
222+
(Collection) convertedValue, propertyName, requiredType, typeDescriptor);
203223
}
204224
else if (convertedValue instanceof Map) {
205225
// Convert keys and values to respective target type, if determined.
206226
convertedValue = convertToTypedMap(
207-
(Map) convertedValue, propertyName, requiredType, methodParam);
227+
(Map) convertedValue, propertyName, requiredType, typeDescriptor);
208228
}
209229
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
210230
convertedValue = Array.get(convertedValue, 0);
@@ -316,10 +336,11 @@ private Object attemptToConvertStringToEnum(Class<?> requiredType, String trimme
316336
* @param descriptor the JavaBeans descriptor for the property
317337
* @return the corresponding editor, or <code>null</code> if none
318338
*/
319-
protected PropertyEditor findDefaultEditor(Class requiredType, PropertyDescriptor descriptor) {
339+
protected PropertyEditor findDefaultEditor(Class requiredType, TypeDescriptor typeDescriptor) {
320340
PropertyEditor editor = null;
321-
if (descriptor != null) {
322-
editor = descriptor.createPropertyEditor(this.targetObject);
341+
if (typeDescriptor instanceof BeanTypeDescriptor) {
342+
PropertyDescriptor pd = ((BeanTypeDescriptor) typeDescriptor).getPropertyDescriptor();
343+
editor = pd.createPropertyEditor(this.targetObject);
323344
}
324345
if (editor == null && requiredType != null) {
325346
// No custom editor -> check BeanWrapperImpl's default editors.
@@ -484,9 +505,10 @@ else if (input.getClass().isArray()) {
484505

485506
@SuppressWarnings("unchecked")
486507
protected Collection convertToTypedCollection(
487-
Collection original, String propertyName, Class requiredType, MethodParameter methodParam) {
508+
Collection original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) {
488509

489510
boolean originalAllowed = requiredType.isInstance(original);
511+
MethodParameter methodParam = typeDescriptor.getMethodParameter();
490512
Class elementType = null;
491513
if (methodParam != null) {
492514
elementType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
@@ -540,7 +562,7 @@ protected Collection convertToTypedCollection(
540562
methodParam.increaseNestingLevel();
541563
}
542564
Object convertedElement =
543-
convertIfNecessary(indexedPropertyName, null, element, elementType, null, methodParam);
565+
convertIfNecessary(indexedPropertyName, null, element, elementType, typeDescriptor);
544566
if (methodParam != null) {
545567
methodParam.decreaseNestingLevel();
546568
}
@@ -551,10 +573,11 @@ protected Collection convertToTypedCollection(
551573
}
552574

553575
@SuppressWarnings("unchecked")
554-
protected Map convertToTypedMap(Map original, String propertyName, Class requiredType, MethodParameter methodParam) {
576+
protected Map convertToTypedMap(Map original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) {
555577
boolean originalAllowed = requiredType.isInstance(original);
556578
Class keyType = null;
557579
Class valueType = null;
580+
MethodParameter methodParam = typeDescriptor.getMethodParameter();
558581
if (methodParam != null) {
559582
keyType = GenericCollectionTypeResolver.getMapKeyParameterType(methodParam);
560583
valueType = GenericCollectionTypeResolver.getMapValueParameterType(methodParam);
@@ -609,11 +632,11 @@ protected Map convertToTypedMap(Map original, String propertyName, Class require
609632
methodParam.increaseNestingLevel();
610633
methodParam.setTypeIndexForCurrentLevel(0);
611634
}
612-
Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType, null, methodParam);
635+
Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType, typeDescriptor);
613636
if (methodParam != null) {
614637
methodParam.setTypeIndexForCurrentLevel(1);
615638
}
616-
Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType, null, methodParam);
639+
Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType, typeDescriptor);
617640
if (methodParam != null) {
618641
methodParam.decreaseNestingLevel();
619642
}

0 commit comments

Comments
 (0)