Skip to content

Commit 836e3c9

Browse files
committed
refined collection element conversion (SPR-8674)
1 parent 2006eec commit 836e3c9

File tree

2 files changed

+138
-134
lines changed

2 files changed

+138
-134
lines changed

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ public <T> T convertIfNecessary(String propertyName, Object oldValue, Object new
138138
if (editor == null && conversionService != null && convertedValue != null) {
139139
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(convertedValue);
140140
TypeDescriptor targetTypeDesc = typeDescriptor;
141+
if (requiredType != null && !requiredType.isAssignableFrom(typeDescriptor.getType())) {
142+
targetTypeDesc = typeDescriptor.forElementType(requiredType);
143+
}
141144
if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
142145
return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
143146
}
@@ -529,7 +532,7 @@ protected Collection convertToTypedCollection(
529532
methodParam.increaseNestingLevel();
530533
}
531534
Object convertedElement = convertIfNecessary(
532-
indexedPropertyName, null, element, elementType, typeDescriptor.getElementTypeDescriptor(element));
535+
indexedPropertyName, null, element, elementType, typeDescriptor);
533536
if (methodParam != null) {
534537
methodParam.decreaseNestingLevel();
535538
}
@@ -623,13 +626,11 @@ protected Map convertToTypedMap(
623626
methodParam.increaseNestingLevel();
624627
methodParam.setTypeIndexForCurrentLevel(0);
625628
}
626-
Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType,
627-
typeDescriptor.getMapKeyTypeDescriptor(key));
629+
Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType, typeDescriptor);
628630
if (methodParam != null) {
629631
methodParam.setTypeIndexForCurrentLevel(1);
630632
}
631-
Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType,
632-
typeDescriptor.getMapValueTypeDescriptor(value));
633+
Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType, typeDescriptor);
633634
if (methodParam != null) {
634635
methodParam.decreaseNestingLevel();
635636
}
Lines changed: 132 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,132 @@
1-
/*
2-
* Copyright 2002-2010 the original author or authors.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
16-
17-
package org.springframework.core.convert.support;
18-
19-
import java.beans.PropertyDescriptor;
20-
import java.lang.annotation.Annotation;
21-
import java.lang.reflect.Field;
22-
import java.lang.reflect.Method;
23-
import java.util.LinkedHashMap;
24-
import java.util.Map;
25-
26-
import org.springframework.core.MethodParameter;
27-
import org.springframework.core.convert.TypeDescriptor;
28-
import org.springframework.util.ReflectionUtils;
29-
import org.springframework.util.StringUtils;
30-
31-
/**
32-
* {@link TypeDescriptor} extension that exposes additional annotations
33-
* as conversion metadata: namely, annotations on other accessor methods
34-
* (getter/setter) and on the underlying field, if found.
35-
*
36-
* @author Juergen Hoeller
37-
* @since 3.0.2
38-
*/
39-
public class PropertyTypeDescriptor extends TypeDescriptor {
40-
41-
private final PropertyDescriptor propertyDescriptor;
42-
43-
private Annotation[] cachedAnnotations;
44-
45-
46-
/**
47-
* Create a new BeanTypeDescriptor for the given bean property.
48-
* @param propertyDescriptor the corresponding JavaBean PropertyDescriptor
49-
* @param methodParameter the target method parameter
50-
*/
51-
public PropertyTypeDescriptor(PropertyDescriptor propertyDescriptor, MethodParameter methodParameter) {
52-
super(methodParameter);
53-
this.propertyDescriptor = propertyDescriptor;
54-
}
55-
56-
/**
57-
* Create a new BeanTypeDescriptor for the given bean property.
58-
* @param propertyDescriptor the corresponding JavaBean PropertyDescriptor
59-
* @param methodParameter the target method parameter
60-
* @param type the specific type to expose (may be an array/collection element)
61-
*/
62-
public PropertyTypeDescriptor(PropertyDescriptor propertyDescriptor, MethodParameter methodParameter, Class<?> type) {
63-
super(methodParameter, type);
64-
this.propertyDescriptor = propertyDescriptor;
65-
}
66-
67-
68-
/**
69-
* Return the underlying PropertyDescriptor.
70-
*/
71-
public PropertyDescriptor getPropertyDescriptor() {
72-
return this.propertyDescriptor;
73-
}
74-
75-
public Annotation[] getAnnotations() {
76-
Annotation[] anns = this.cachedAnnotations;
77-
if (anns == null) {
78-
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>();
79-
String name = this.propertyDescriptor.getName();
80-
if (StringUtils.hasLength(name)) {
81-
Class<?> clazz = getMethodParameter().getMethod().getDeclaringClass();
82-
Field field = ReflectionUtils.findField(clazz, name);
83-
if (field == null) {
84-
// Same lenient fallback checking as in CachedIntrospectionResults...
85-
field = ReflectionUtils.findField(clazz, name.substring(0, 1).toLowerCase() + name.substring(1));
86-
if (field == null) {
87-
field = ReflectionUtils.findField(clazz, name.substring(0, 1).toUpperCase() + name.substring(1));
88-
}
89-
}
90-
if (field != null) {
91-
for (Annotation ann : field.getAnnotations()) {
92-
annMap.put(ann.annotationType(), ann);
93-
}
94-
}
95-
}
96-
Method writeMethod = this.propertyDescriptor.getWriteMethod();
97-
Method readMethod = this.propertyDescriptor.getReadMethod();
98-
if (writeMethod != null && writeMethod != getMethodParameter().getMethod()) {
99-
for (Annotation ann : writeMethod.getAnnotations()) {
100-
annMap.put(ann.annotationType(), ann);
101-
}
102-
}
103-
if (readMethod != null && readMethod != getMethodParameter().getMethod()) {
104-
for (Annotation ann : readMethod.getAnnotations()) {
105-
annMap.put(ann.annotationType(), ann);
106-
}
107-
}
108-
for (Annotation ann : getMethodParameter().getMethodAnnotations()) {
109-
annMap.put(ann.annotationType(), ann);
110-
}
111-
for (Annotation ann : getMethodParameter().getParameterAnnotations()) {
112-
annMap.put(ann.annotationType(), ann);
113-
}
114-
anns = annMap.values().toArray(new Annotation[annMap.size()]);
115-
this.cachedAnnotations = anns;
116-
}
117-
return anns;
118-
}
119-
120-
public TypeDescriptor forElementType(Class<?> elementType) {
121-
if (elementType != null) {
122-
return new PropertyTypeDescriptor(this.propertyDescriptor, getMethodParameter(), elementType);
123-
}
124-
else {
125-
return super.forElementType(null);
126-
}
127-
}
128-
129-
}
1+
/*
2+
* Copyright 2002-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.convert.support;
18+
19+
import java.beans.PropertyDescriptor;
20+
import java.lang.annotation.Annotation;
21+
import java.lang.reflect.Field;
22+
import java.lang.reflect.Method;
23+
import java.util.LinkedHashMap;
24+
import java.util.Map;
25+
26+
import org.springframework.core.MethodParameter;
27+
import org.springframework.core.convert.TypeDescriptor;
28+
import org.springframework.util.ReflectionUtils;
29+
import org.springframework.util.StringUtils;
30+
31+
/**
32+
* {@link TypeDescriptor} extension that exposes additional annotations
33+
* as conversion metadata: namely, annotations on other accessor methods
34+
* (getter/setter) and on the underlying field, if found.
35+
*
36+
* @author Juergen Hoeller
37+
* @since 3.0.2
38+
*/
39+
public class PropertyTypeDescriptor extends TypeDescriptor {
40+
41+
private final PropertyDescriptor propertyDescriptor;
42+
43+
private Annotation[] cachedAnnotations;
44+
45+
46+
/**
47+
* Create a new BeanTypeDescriptor for the given bean property.
48+
* @param propertyDescriptor the corresponding JavaBean PropertyDescriptor
49+
* @param methodParameter the target method parameter
50+
*/
51+
public PropertyTypeDescriptor(PropertyDescriptor propertyDescriptor, MethodParameter methodParameter) {
52+
super(methodParameter);
53+
this.propertyDescriptor = propertyDescriptor;
54+
}
55+
56+
/**
57+
* Create a new BeanTypeDescriptor for the given bean property.
58+
* @param propertyDescriptor the corresponding JavaBean PropertyDescriptor
59+
* @param methodParameter the target method parameter
60+
* @param type the specific type to expose (may be an array/collection element)
61+
*/
62+
public PropertyTypeDescriptor(PropertyDescriptor propertyDescriptor, MethodParameter methodParameter, Class<?> type) {
63+
super(methodParameter, type);
64+
this.propertyDescriptor = propertyDescriptor;
65+
}
66+
67+
68+
/**
69+
* Return the underlying PropertyDescriptor.
70+
*/
71+
public PropertyDescriptor getPropertyDescriptor() {
72+
return this.propertyDescriptor;
73+
}
74+
75+
public Annotation[] getAnnotations() {
76+
Annotation[] anns = this.cachedAnnotations;
77+
if (anns == null) {
78+
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>();
79+
String name = this.propertyDescriptor.getName();
80+
if (StringUtils.hasLength(name)) {
81+
Class<?> clazz = getMethodParameter().getMethod().getDeclaringClass();
82+
Field field = ReflectionUtils.findField(clazz, name);
83+
if (field == null) {
84+
// Same lenient fallback checking as in CachedIntrospectionResults...
85+
field = ReflectionUtils.findField(clazz, name.substring(0, 1).toLowerCase() + name.substring(1));
86+
if (field == null) {
87+
field = ReflectionUtils.findField(clazz, name.substring(0, 1).toUpperCase() + name.substring(1));
88+
}
89+
}
90+
if (field != null) {
91+
for (Annotation ann : field.getAnnotations()) {
92+
annMap.put(ann.annotationType(), ann);
93+
}
94+
}
95+
}
96+
Method writeMethod = this.propertyDescriptor.getWriteMethod();
97+
Method readMethod = this.propertyDescriptor.getReadMethod();
98+
if (writeMethod != null && writeMethod != getMethodParameter().getMethod()) {
99+
for (Annotation ann : writeMethod.getAnnotations()) {
100+
annMap.put(ann.annotationType(), ann);
101+
}
102+
}
103+
if (readMethod != null && readMethod != getMethodParameter().getMethod()) {
104+
for (Annotation ann : readMethod.getAnnotations()) {
105+
annMap.put(ann.annotationType(), ann);
106+
}
107+
}
108+
for (Annotation ann : getMethodParameter().getMethodAnnotations()) {
109+
annMap.put(ann.annotationType(), ann);
110+
}
111+
for (Annotation ann : getMethodParameter().getParameterAnnotations()) {
112+
annMap.put(ann.annotationType(), ann);
113+
}
114+
anns = annMap.values().toArray(new Annotation[annMap.size()]);
115+
this.cachedAnnotations = anns;
116+
}
117+
return anns;
118+
}
119+
120+
@Override
121+
public TypeDescriptor forElementType(Class<?> elementType) {
122+
if (elementType != null) {
123+
MethodParameter nested = new MethodParameter(getMethodParameter());
124+
nested.increaseNestingLevel();
125+
return new PropertyTypeDescriptor(this.propertyDescriptor, nested, elementType);
126+
}
127+
else {
128+
return super.forElementType(null);
129+
}
130+
}
131+
132+
}

0 commit comments

Comments
 (0)