added forNestedType(MethodParameter) for resolution of nested parameter types for collection, array, and map parameter types

This commit is contained in:
Keith Donald 2011-01-06 21:59:34 +00:00
parent 61cfb96d95
commit 4c9731d572
6 changed files with 91 additions and 35 deletions

View File

@ -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);
}
}
}
}

View File

@ -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"));
}

View File

@ -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();

View File

@ -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() {

View File

@ -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.
*/

View File

@ -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));