added forNestedType(MethodParameter) for resolution of nested parameter types for collection, array, and map parameter types
This commit is contained in:
parent
61cfb96d95
commit
4c9731d572
|
|
@ -364,14 +364,27 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
|
|||
|
||||
public TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException {
|
||||
try {
|
||||
String actualPropertyName = PropertyAccessorUtils.getPropertyName(propertyName);
|
||||
PropertyDescriptor pd = getPropertyDescriptorInternal(actualPropertyName);
|
||||
PropertyTokenHolder tokens = getPropertyNameTokens(propertyName);
|
||||
PropertyDescriptor pd = getPropertyDescriptorInternal(tokens.actualName);
|
||||
if (pd != null) {
|
||||
if (pd.getReadMethod() != null) {
|
||||
return new PropertyTypeDescriptor(new MethodParameter(pd.getReadMethod(), -1), pd);
|
||||
}
|
||||
else if (pd.getWriteMethod() != null) {
|
||||
return new PropertyTypeDescriptor(BeanUtils.getWriteMethodParameter(pd), pd);
|
||||
if (tokens.keys != null) {
|
||||
if (pd.getReadMethod() != null) {
|
||||
return PropertyTypeDescriptor.forNestedType(new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length), pd);
|
||||
}
|
||||
else if (pd.getWriteMethod() != null) {
|
||||
MethodParameter methodParameter = new MethodParameter(BeanUtils.getWriteMethodParameter(pd));
|
||||
for (int i = 0; i < tokens.keys.length; i++) {
|
||||
methodParameter.increaseNestingLevel();
|
||||
}
|
||||
return PropertyTypeDescriptor.forNestedType(methodParameter, pd);
|
||||
}
|
||||
} else {
|
||||
if (pd.getReadMethod() != null) {
|
||||
return new PropertyTypeDescriptor(new MethodParameter(pd.getReadMethod(), -1), pd);
|
||||
}
|
||||
else if (pd.getWriteMethod() != null) {
|
||||
return new PropertyTypeDescriptor(BeanUtils.getWriteMethodParameter(pd), pd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ public class NumberFormattingTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testPatternArrayFormatting() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("patternArray", new String[] {"1,25.00", "2,35.00"});
|
||||
|
|
@ -135,7 +134,6 @@ public class NumberFormattingTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testPatternListFormatting() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("patternList", new String[] {"1,25.00", "2,35.00"});
|
||||
|
|
@ -154,15 +152,24 @@ public class NumberFormattingTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testPatternList2Formatting() {
|
||||
public void testPatternList2FormattingListElement() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("patternList2[0]", "1,25.00");
|
||||
propertyValues.add("patternList2[1]", "2,35.00");
|
||||
binder.bind(propertyValues);
|
||||
assertEquals(0, binder.getBindingResult().getErrorCount());
|
||||
assertEquals("1,25.00", binder.getBindingResult().getFieldValue("patternList[0]"));
|
||||
assertEquals("2,35.00", binder.getBindingResult().getFieldValue("patternList[1]"));
|
||||
assertEquals("1,25.00", binder.getBindingResult().getFieldValue("patternList2[0]"));
|
||||
assertEquals("2,35.00", binder.getBindingResult().getFieldValue("patternList2[1]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatternList2FormattingList() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("patternList2[0]", "1,25.00");
|
||||
propertyValues.add("patternList2[1]", "2,35.00");
|
||||
binder.bind(propertyValues);
|
||||
assertEquals(0, binder.getBindingResult().getErrorCount());
|
||||
assertEquals("1,25.00,2,35.00", binder.getBindingResult().getFieldValue("patternList2"));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ public class DataBinderTests extends TestCase {
|
|||
try {
|
||||
binder.bind(pvs);
|
||||
assertEquals(new Integer(1), tb.getIntegerList().get(0));
|
||||
// TODO add back assertEquals("1", binder.getBindingResult().getFieldValue("integerList[0]"));
|
||||
assertEquals("1", binder.getBindingResult().getFieldValue("integerList[0]"));
|
||||
}
|
||||
finally {
|
||||
LocaleContextHolder.resetLocaleContext();
|
||||
|
|
|
|||
|
|
@ -105,10 +105,26 @@ public class TypeDescriptor {
|
|||
this.type = field.getType();
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new type descriptor for the given class.
|
||||
* Use this to instruct the conversion system to convert to an object to a specific target type, when no type location such as a method parameter or field is available to provide additional conversion context.
|
||||
* Generally prefer use of {@link #forObject(Object)} for constructing source type descriptors for source objects.
|
||||
* @param type the class
|
||||
* @return the type descriptor
|
||||
*/
|
||||
public static TypeDescriptor valueOf(Class<?> type) {
|
||||
if (type == null) {
|
||||
return NULL;
|
||||
}
|
||||
TypeDescriptor desc = typeDescriptorCache.get(type);
|
||||
return (desc != null ? desc : new TypeDescriptor(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new type descriptor for object.
|
||||
* Create a new type descriptor for an object.
|
||||
* Use this factory method to introspect a source object's type before asking the conversion system to convert it to some another type.
|
||||
* Builds in population of nested type descriptors for collection and map objects through object introspection.
|
||||
* If the object is null, returns {@link TypeDescriptor#NULL}.
|
||||
* If the object is not a collection, simply calls {@link #valueOf(Class)}.
|
||||
* If the object is a collection, this factory method will derive the element type(s) by introspecting the collection.
|
||||
|
|
@ -132,6 +148,16 @@ public class TypeDescriptor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new type descriptor for a nested type declared on an array, collection, or map-based method parameter.
|
||||
* Use this factory method when you've resolved a nested source object such as a collection element or map value and wish to have it converted.
|
||||
* @param methodParameter the method parameter declaring the collection or map
|
||||
* @return the nested type descriptor
|
||||
*/
|
||||
public static TypeDescriptor forNestedType(MethodParameter methodParameter) {
|
||||
return new TypeDescriptor(resolveNestedType(methodParameter), methodParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new type descriptor for a nested type declared on an array, collection, or map-based method parameter.
|
||||
* Use this factory method when you've resolved a nested source object such as a collection element or map value and wish to have it converted.
|
||||
|
|
@ -142,22 +168,7 @@ public class TypeDescriptor {
|
|||
public static TypeDescriptor forNestedType(Class<?> nestedType, MethodParameter methodParameter) {
|
||||
return new TypeDescriptor(nestedType, methodParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new type descriptor for the given class.
|
||||
* Use this to instruct the conversion system to convert to an object to a specific target type, when no type location such as a method parameter or field is available.
|
||||
* Generally prefer use of {@link #forObject(Object)} for constructing source type descriptors for source objects.
|
||||
* @param type the class
|
||||
* @return the type descriptor
|
||||
*/
|
||||
public static TypeDescriptor valueOf(Class<?> type) {
|
||||
if (type == null) {
|
||||
return NULL;
|
||||
}
|
||||
TypeDescriptor desc = typeDescriptorCache.get(type);
|
||||
return (desc != null ? desc : new TypeDescriptor(type));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the declared (non-generic) type of the wrapped parameter/field.
|
||||
* @return the declared type, or <code>null</code> if this is {@link TypeDescriptor#NULL}
|
||||
|
|
@ -436,6 +447,18 @@ public class TypeDescriptor {
|
|||
return new TypeDescriptor(nestedType, nested);
|
||||
}
|
||||
|
||||
protected static Class<?> resolveNestedType(MethodParameter methodParameter) {
|
||||
if (Collection.class.isAssignableFrom(methodParameter.getParameterType())) {
|
||||
return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter);
|
||||
} else if (Map.class.isAssignableFrom(methodParameter.getParameterType())) {
|
||||
return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter);
|
||||
} else if (methodParameter.getParameterType().isArray()) {
|
||||
return methodParameter.getParameterType().getComponentType();
|
||||
} else {
|
||||
throw new IllegalStateException("Not a collection, map, or array method parameter type " + methodParameter.getParameterType());
|
||||
}
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private TypeDescriptor resolveElementTypeDescriptor() {
|
||||
|
|
|
|||
|
|
@ -20,9 +20,11 @@ import java.beans.PropertyDescriptor;
|
|||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.GenericCollectionTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
|
@ -51,18 +53,29 @@ public class PropertyTypeDescriptor extends TypeDescriptor {
|
|||
this.propertyDescriptor = propertyDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new type descriptor for a nested type declared on an array, collection, or map-based property.
|
||||
* Use this factory method when you've resolved a nested source object such as a collection element or map value and wish to have it converted.
|
||||
* Builds in protection for increasing the nesting level of the provided MethodParameter if the nestedType is itself a collection.
|
||||
* @param methodParameter the method parameter
|
||||
* @return the property descriptor
|
||||
*/
|
||||
public static PropertyTypeDescriptor forNestedType(MethodParameter methodParameter, PropertyDescriptor propertyDescriptor) {
|
||||
return forNestedType(resolveNestedType(methodParameter), methodParameter, propertyDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new type descriptor for a nested type declared on an array, collection, or map-based property.
|
||||
* Use this factory method when you've resolved a nested source object such as a collection element or map value and wish to have it converted.
|
||||
* Builds in protection for increasing the nesting level of the provided MethodParameter if the nestedType is itself a collection.
|
||||
* @param nestedType the nested type
|
||||
* @param parentMethodParameter the method parameter
|
||||
* @param methodParameter the method parameter
|
||||
* @return the property descriptor
|
||||
*/
|
||||
public static PropertyTypeDescriptor forNestedType(Class<?> nestedType, MethodParameter methodParameter, PropertyDescriptor propertyDescriptor) {
|
||||
return new PropertyTypeDescriptor(nestedType, methodParameter, propertyDescriptor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the underlying PropertyDescriptor.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ public class ReflectionHelper {
|
|||
TypeDescriptor targetType;
|
||||
if (varargsPosition != null && argPosition >= varargsPosition) {
|
||||
MethodParameter methodParam = MethodParameter.forMethodOrConstructor(methodOrCtor, varargsPosition);
|
||||
targetType = TypeDescriptor.forNestedType(methodParam.getParameterType().getComponentType(), methodParam);
|
||||
targetType = TypeDescriptor.forNestedType(methodParam);
|
||||
}
|
||||
else {
|
||||
targetType = new TypeDescriptor(MethodParameter.forMethodOrConstructor(methodOrCtor, argPosition));
|
||||
|
|
@ -268,7 +268,7 @@ public class ReflectionHelper {
|
|||
TypeDescriptor targetType;
|
||||
if (varargsPosition != null && argPosition >= varargsPosition) {
|
||||
MethodParameter methodParam = new MethodParameter(method, varargsPosition);
|
||||
targetType = TypeDescriptor.forNestedType(methodParam.getParameterType().getComponentType(), methodParam);
|
||||
targetType = TypeDescriptor.forNestedType(methodParam);
|
||||
}
|
||||
else {
|
||||
targetType = new TypeDescriptor(new MethodParameter(method, argPosition));
|
||||
|
|
|
|||
Loading…
Reference in New Issue