diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java
index b42d41205db..35b41881a82 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java
@@ -369,10 +369,10 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
if (pd != null) {
Class type = getPropertyType(propertyName);
if (pd.getReadMethod() != null) {
- return new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), type);
+ return new PropertyTypeDescriptor(type, new MethodParameter(pd.getReadMethod(), -1), pd);
}
else if (pd.getWriteMethod() != null) {
- return new PropertyTypeDescriptor(pd, BeanUtils.getWriteMethodParameter(pd), type);
+ return new PropertyTypeDescriptor(type, BeanUtils.getWriteMethodParameter(pd), pd);
}
}
}
@@ -468,12 +468,6 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
}
- private Object convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class> requiredType)
- throws TypeMismatchException {
-
- return convertIfNecessary(propertyName, oldValue, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
- }
-
/**
* Convert the given value for the specified property to the latter's type.
*
This method is only intended for optimizations in a BeanFactory.
@@ -497,7 +491,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
throws TypeMismatchException {
return convertIfNecessary(propertyName, oldValue, newValue, pd.getPropertyType(),
- new PropertyTypeDescriptor(pd, BeanUtils.getWriteMethodParameter(pd)));
+ new PropertyTypeDescriptor(BeanUtils.getWriteMethodParameter(pd), pd));
}
@@ -794,7 +788,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType,
- new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), mapKeyType));
+ new PropertyTypeDescriptor(mapKeyType, new MethodParameter(pd.getReadMethod(), -1), pd));
value = map.get(convertedMapKey);
}
else {
@@ -942,7 +936,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
oldValue = Array.get(propValue, arrayIndex);
}
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType,
- new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), requiredType));
+ new PropertyTypeDescriptor(requiredType, new MethodParameter(pd.getReadMethod(), -1), pd));
Array.set(propValue, arrayIndex, convertedValue);
}
catch (IndexOutOfBoundsException ex) {
@@ -961,7 +955,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
oldValue = list.get(index);
}
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType,
- new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), requiredType));
+ new PropertyTypeDescriptor(requiredType, new MethodParameter(pd.getReadMethod(), -1), pd));
if (index < list.size()) {
list.set(index, convertedValue);
}
@@ -990,7 +984,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType,
- new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), mapKeyType));
+ new PropertyTypeDescriptor(mapKeyType, new MethodParameter(pd.getReadMethod(), -1), pd));
Object oldValue = null;
if (isExtractOldValueForEditor()) {
oldValue = map.get(convertedMapKey);
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java
index dedda9eb1f1..a676439cbe9 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java
@@ -136,7 +136,7 @@ class TypeConverterDelegate {
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && convertedValue != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(convertedValue);
- TypeDescriptor targetTypeDesc = typeDescriptor.forElementType(requiredType);
+ TypeDescriptor targetTypeDesc = typeDescriptor;
if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
}
diff --git a/org.springframework.context/.classpath b/org.springframework.context/.classpath
index 9eb9e41365f..f9fabe5616c 100644
--- a/org.springframework.context/.classpath
+++ b/org.springframework.context/.classpath
@@ -1,39 +1,42 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java
index 59335f2b7a1..d91ed06f000 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java
@@ -42,9 +42,6 @@ public class TypeDescriptor {
/** Constant defining a TypeDescriptor for a null value */
public static final TypeDescriptor NULL = new TypeDescriptor();
- /** Constant defining a TypeDescriptor for 'unknown type' */
- private static final TypeDescriptor UNKNOWN = new TypeDescriptor(Object.class);
-
private static final Map, TypeDescriptor> typeDescriptorCache = new HashMap, TypeDescriptor>();
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
@@ -78,8 +75,6 @@ public class TypeDescriptor {
private int fieldNestingLevel = 1;
- private Object value;
-
private TypeDescriptor elementType;
private TypeDescriptor mapKeyType;
@@ -97,6 +92,7 @@ public class TypeDescriptor {
*/
public TypeDescriptor(MethodParameter methodParameter) {
Assert.notNull(methodParameter, "MethodParameter must not be null");
+ this.type = methodParameter.getParameterType();
this.methodParameter = methodParameter;
}
@@ -107,145 +103,132 @@ public class TypeDescriptor {
*/
public TypeDescriptor(Field field) {
Assert.notNull(field, "Field must not be null");
+ this.type = field.getType();
this.field = field;
}
/**
- * Create a new type descriptor from a method or constructor parameter.
- *
Use this constructor when a target conversion point originates from a method parameter,
- * such as a setter method argument.
- * @param methodParameter the MethodParameter to wrap
- * @param type the specific type to expose (may be an array/collection element)
+ * Create a new type descriptor for the given class.
+ * @param type the class
+ * @return the type descriptor
*/
- public TypeDescriptor(MethodParameter methodParameter, Class> type) {
- Assert.notNull(methodParameter, "MethodParameter must not be null");
- this.methodParameter = methodParameter;
- this.type = type;
+ 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 a field.
- * Use this constructor when a target conversion point originates from a field.
- * @param field the field to wrap
- * @param type the specific type to expose (may be an array/collection element)
+ * Create a new type descriptor for the class of the given object.
+ * @param object the object
+ * @return the type descriptor
*/
- public TypeDescriptor(Field field, Class> type) {
- Assert.notNull(field, "Field must not be null");
- this.field = field;
- this.type = type;
+ public static TypeDescriptor forObject(Object object) {
+ if (object == null) {
+ return NULL;
+ }
+ if (object instanceof Collection>) {
+ return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType((Collection>) object));
+ }
+ else if (object instanceof Map, ?>) {
+ return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType(((Map, ?>) object).keySet()), CollectionUtils.findCommonElementType(((Map, ?>) object).values()));
+ }
+ else {
+ return valueOf(object.getClass());
+ }
}
-
+
/**
- * Create a new type descriptor for a field.
- * Use this constructor when a target conversion point originates from a field.
- * @param field the field to wrap
- * @param type the specific type to expose (may be an array/collection element)
+ * Determine the declared (non-generic) type of the wrapped parameter/field.
+ * @return the declared type, or null if this is {@link TypeDescriptor#NULL}
*/
- private TypeDescriptor(Field field, int nestingLevel, Class> type) {
- Assert.notNull(field, "Field must not be null");
- this.field = field;
- this.fieldNestingLevel = nestingLevel;
- this.type = type;
+ public Class> getType() {
+ return type;
}
/**
- * Internal constructor for a NULL descriptor.
+ * Determine the declared type of the wrapped parameter/field.
+ * Returns the Object wrapper type if the underlying type is a primitive.
*/
- private TypeDescriptor() {
+ public Class> getObjectType() {
+ return ClassUtils.resolvePrimitiveIfNecessary(getType());
}
/**
- * Create a new descriptor for the type of the given value.
- *
Use this constructor when a conversion point comes from a source such as a Map or
- * Collection, where no additional context is available but elements can be introspected.
- * @param value the value to determine the actual type from
+ * Returns the name of this type: the fully qualified class name.
*/
- private TypeDescriptor(Object value) {
- Assert.notNull(value, "Value must not be null");
- this.value = value;
- this.type = value.getClass();
+ public String getName() {
+ return ClassUtils.getQualifiedName(getType());
}
/**
- * Create a new descriptor for the given type.
- *
Use this constructor when a conversion point comes from a plain source type,
- * where no additional context is available.
- * @param type the actual type to wrap
+ * Is this type a primitive type?
*/
- private TypeDescriptor(Class> type) {
- Assert.notNull(type, "Type must not be null");
- this.type = type;
+ public boolean isPrimitive() {
+ return getType().isPrimitive();
}
-
/**
- * Return the wrapped MethodParameter, if any.
- *
Note: Either MethodParameter or Field is available.
- * @return the MethodParameter, or null if none
+ * Obtain the annotations associated with the wrapped parameter/field, if any.
*/
- public MethodParameter getMethodParameter() {
- return this.methodParameter;
+ public synchronized Annotation[] getAnnotations() {
+ if (this.annotations == null) {
+ this.annotations = resolveAnnotations();
+ }
+ return this.annotations;
}
/**
- * Return the wrapped Field, if any.
- *
Note: Either MethodParameter or Field is available.
- * @return the Field, or null if none
+ * Obtain the annotation associated with the wrapped parameter/field, if any.
*/
- public Field getField() {
- return this.field;
+ public Annotation getAnnotation(Class extends Annotation> annotationType) {
+ for (Annotation annotation : getAnnotations()) {
+ if (annotation.annotationType().equals(annotationType)) {
+ return annotation;
+ }
+ }
+ return null;
}
/**
- * Determine the declared (non-generic) type of the wrapped parameter/field.
- * @return the declared type, or null if this is {@link TypeDescriptor#NULL}
+ * Returns true if an object of this type can be assigned to a reference of given targetType.
+ * @param targetType the target type
+ * @return true if this type is assignable to the target
*/
- public Class> getType() {
- if (this.type != null) {
- return this.type;
+ public boolean isAssignableTo(TypeDescriptor targetType) {
+ if (this == TypeDescriptor.NULL || targetType == TypeDescriptor.NULL) {
+ return true;
}
- else if (this.field != null) {
- return this.field.getType();
+ if (isCollection() && targetType.isCollection() || isArray() && targetType.isArray()) {
+ return targetType.getType().isAssignableFrom(getType()) &&
+ getElementTypeDescriptor().isAssignableTo(targetType.getElementTypeDescriptor());
}
- else if (this.methodParameter != null) {
- return this.methodParameter.getParameterType();
+ else if (isMap() && targetType.isMap()) {
+ return targetType.getType().isAssignableFrom(getType()) &&
+ getMapKeyTypeDescriptor().isAssignableTo(targetType.getMapKeyTypeDescriptor()) &&
+ getMapValueTypeDescriptor().isAssignableTo(targetType.getMapValueTypeDescriptor());
}
else {
- return null;
+ return targetType.getObjectType().isAssignableFrom(getObjectType());
}
}
/**
- * Determine the declared type of the wrapped parameter/field.
- * Returns the Object wrapper type if the underlying type is a primitive.
- */
- public Class> getObjectType() {
- Class> type = getType();
- return (type != null ? ClassUtils.resolvePrimitiveIfNecessary(type) : type);
- }
-
- /**
- * Returns the name of this type: the fully qualified class name.
- */
- public String getName() {
- Class> type = getType();
- return (type != null ? ClassUtils.getQualifiedName(type) : null);
- }
-
- /**
- * Is this type a primitive type?
+ * A textual representation of the type descriptor (eg. Map) for use in messages.
*/
- public boolean isPrimitive() {
- Class> type = getType();
- return (type != null && type.isPrimitive());
+ public String asString() {
+ return toString();
}
-
+
+ // indexable type descriptor operations
+
/**
* Is this type an array type?
*/
public boolean isArray() {
- Class> type = getType();
- return (type != null && type.isArray());
+ return getType().isArray();
}
/**
@@ -267,23 +250,17 @@ public class TypeDescriptor {
* Return the element type as a type descriptor.
*/
public synchronized TypeDescriptor getElementTypeDescriptor() {
+ if (!isCollection() && !isArray()) {
+ throw new IllegalStateException("Not a collection or array type");
+ }
if (this.elementType == null) {
- this.elementType = forElementType(resolveElementType());
+ this.elementType = resolveElementTypeDescriptor();
}
return this.elementType;
}
- /**
- * Return the element type as a type descriptor. If the element type is null
- * (cannot be determined), the type descriptor is derived from the element argument.
- * @param element the element
- * @return the element type descriptor
- */
- public TypeDescriptor getElementTypeDescriptor(Object element) {
- TypeDescriptor elementType = getElementTypeDescriptor();
- return (elementType != TypeDescriptor.UNKNOWN ? elementType : forObject(element));
- }
-
+ // map type descriptor operations
+
/**
* Is this type a {@link Map} type?
*/
@@ -291,13 +268,6 @@ public class TypeDescriptor {
return Map.class.isAssignableFrom(getType());
}
- /**
- * Is this descriptor for a map where the key type and value type are known?
- */
- public boolean isMapEntryTypeKnown() {
- return (isMap() && getMapKeyType() != null && getMapValueType() != null);
- }
-
/**
* Determine the generic key type of the wrapped Map parameter/field, if any.
* @return the generic type, or null if none
@@ -310,23 +280,15 @@ public class TypeDescriptor {
* Returns map key type as a type descriptor.
*/
public synchronized TypeDescriptor getMapKeyTypeDescriptor() {
+ if (!isMap()) {
+ throw new IllegalStateException("Not a Map type");
+ }
if (this.mapKeyType == null) {
- this.mapKeyType = forElementType(resolveMapKeyType());
+ this.mapKeyType = resolveMapKeyTypeDescriptor();
}
return this.mapKeyType;
}
- /**
- * Return the map key type as a type descriptor. If the key type is null
- * (cannot be determined), the type descriptor is derived from the key argument.
- * @param key the key
- * @return the map key type descriptor
- */
- public TypeDescriptor getMapKeyTypeDescriptor(Object key) {
- TypeDescriptor keyType = getMapKeyTypeDescriptor();
- return (keyType != TypeDescriptor.UNKNOWN ? keyType : TypeDescriptor.forObject(key));
- }
-
/**
* Determine the generic value type of the wrapped Map parameter/field, if any.
* @return the generic type, or null if none
@@ -340,90 +302,44 @@ public class TypeDescriptor {
*/
public synchronized TypeDescriptor getMapValueTypeDescriptor() {
if (this.mapValueType == null) {
- this.mapValueType = forElementType(resolveMapValueType());
+ this.mapValueType = resolveMapValueTypeDescriptor();
}
return this.mapValueType;
}
- /**
- * Return the map value type as a type descriptor. If the value type is null
- * (cannot be determined), the type descriptor is derived from the value argument.
- * @param value the value
- * @return the map value type descriptor
- */
- public TypeDescriptor getMapValueTypeDescriptor(Object value) {
- TypeDescriptor valueType = getMapValueTypeDescriptor();
- return (valueType != TypeDescriptor.UNKNOWN ? valueType : TypeDescriptor.forObject(value));
- }
-
- /**
- * Obtain the annotations associated with the wrapped parameter/field, if any.
- */
- public synchronized Annotation[] getAnnotations() {
- if (this.annotations == null) {
- this.annotations = resolveAnnotations();
- }
- return this.annotations;
- }
+ // special case public operations
- /**
- * Obtain the annotation associated with the wrapped parameter/field, if any.
- */
- public Annotation getAnnotation(Class extends Annotation> annotationType) {
- for (Annotation annotation : getAnnotations()) {
- if (annotation.annotationType().equals(annotationType)) {
- return annotation;
- }
+ public TypeDescriptor(Class> componentType, MethodParameter methodParameter) {
+ if (componentType == null) {
+ componentType = Object.class;
}
- return null;
+ this.type = componentType;
+ this.methodParameter = methodParameter;
}
-
- /**
- * Returns true if an object of this type can be assigned to a reference of given targetType.
- * @param targetType the target type
- * @return true if this type is assignable to the target
- */
- public boolean isAssignableTo(TypeDescriptor targetType) {
- if (this == TypeDescriptor.NULL || targetType == TypeDescriptor.NULL) {
- return true;
- }
- if (isCollection() && targetType.isCollection() || isArray() && targetType.isArray()) {
- return targetType.getType().isAssignableFrom(getType()) &&
- getElementTypeDescriptor().isAssignableTo(targetType.getElementTypeDescriptor());
- }
- else if (isMap() && targetType.isMap()) {
- return targetType.getType().isAssignableFrom(getType()) &&
- getMapKeyTypeDescriptor().isAssignableTo(targetType.getMapKeyTypeDescriptor()) &&
- getMapValueTypeDescriptor().isAssignableTo(targetType.getMapValueTypeDescriptor());
- }
- else {
- return targetType.getObjectType().isAssignableFrom(getObjectType());
- }
+
+ public MethodParameter getMethodParameter() {
+ return methodParameter;
}
- /**
- * Create a copy of this type descriptor, preserving the context information
- * but exposing the specified element type (e.g. an array/collection/map element).
- * @param elementType the desired type to expose
- * @return the type descriptor
- */
- public TypeDescriptor forElementType(Class> elementType) {
- if (elementType == null) {
- return TypeDescriptor.UNKNOWN;
+ public TypeDescriptor applyType(Object object) {
+ if (object == null) {
+ return this;
}
- else if (this.methodParameter != null) {
- MethodParameter nested = new MethodParameter(this.methodParameter);
- nested.increaseNestingLevel();
- return new TypeDescriptor(nested, elementType);
+ // TODO preserve binding context with returned copy
+ // TODO fall back to generic info if collection is empty
+ if (object instanceof Collection>) {
+ return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType((Collection>) object));
}
- else if (this.field != null) {
- return new TypeDescriptor(this.field, this.fieldNestingLevel + 1, elementType);
+ else if (object instanceof Map, ?>) {
+ return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType(((Map, ?>) object).keySet()), CollectionUtils.findCommonElementType(((Map, ?>) object).values()));
}
else {
- return TypeDescriptor.valueOf(elementType);
+ return valueOf(object.getClass());
}
}
-
+
+ // extending Object
+
public boolean equals(Object obj) {
if (this == obj) {
return true;
@@ -432,8 +348,7 @@ public class TypeDescriptor {
return false;
}
TypeDescriptor other = (TypeDescriptor) obj;
- boolean annotatedTypeEquals =
- getType().equals(other.getType()) && ObjectUtils.nullSafeEquals(getAnnotations(), other.getAnnotations());
+ boolean annotatedTypeEquals = getType().equals(other.getType()) && ObjectUtils.nullSafeEquals(getAnnotations(), other.getAnnotations());
if (isCollection()) {
return annotatedTypeEquals && ObjectUtils.nullSafeEquals(getElementType(), other.getElementType());
}
@@ -450,13 +365,6 @@ public class TypeDescriptor {
return (this == TypeDescriptor.NULL ? 0 : getType().hashCode());
}
- /**
- * A textual representation of the type descriptor (eg. Map) for use in messages.
- */
- public String asString() {
- return toString();
- }
-
public String toString() {
if (this == TypeDescriptor.NULL) {
return "null";
@@ -479,129 +387,137 @@ public class TypeDescriptor {
}
}
+ // subclassing hooks
+
+ protected Annotation[] resolveAnnotations() {
+ if (this.field != null) {
+ return this.field.getAnnotations();
+ }
+ else if (this.methodParameter != null) {
+ if (this.methodParameter.getParameterIndex() < 0) {
+ return this.methodParameter.getMethodAnnotations();
+ }
+ else {
+ return this.methodParameter.getParameterAnnotations();
+ }
+ }
+ else {
+ return EMPTY_ANNOTATION_ARRAY;
+ }
+ }
+ protected TypeDescriptor newComponentTypeDescriptor(Class> componentType, MethodParameter nested) {
+ return new TypeDescriptor(componentType, nested);
+ }
+
// internal helpers
- private Class> resolveElementType() {
- if (isArray()) {
- return getType().getComponentType();
+ private TypeDescriptor resolveElementTypeDescriptor() {
+ if (isCollection()) {
+ return createComponentTypeDescriptor(resolveCollectionElementType());
+ }
+ else {
+ // TODO: GenericCollectionTypeResolver is not capable of applying nesting levels to array fields;
+ // this means generic info of nested lists or maps stored inside array method parameters or fields is not obtainable
+ return createComponentTypeDescriptor(getType().getComponentType());
+ }
+ }
+
+ private TypeDescriptor resolveMapKeyTypeDescriptor() {
+ return createComponentTypeDescriptor(resolveMapKeyType());
+ }
+
+ private TypeDescriptor resolveMapValueTypeDescriptor() {
+ return createComponentTypeDescriptor(resolveMapValueType());
+ }
+
+ private TypeDescriptor createComponentTypeDescriptor(Class> componentType) {
+ if (componentType == null) {
+ componentType = Object.class;
+ }
+ if (this.methodParameter != null) {
+ MethodParameter nested = new MethodParameter(this.methodParameter);
+ nested.increaseNestingLevel();
+ return newComponentTypeDescriptor(componentType, nested);
}
- else if (isCollection()) {
- return resolveCollectionElementType();
+ else if (this.field != null) {
+ return new TypeDescriptor(componentType, this.field, this.fieldNestingLevel + 1);
}
else {
- return null;
+ return TypeDescriptor.valueOf(componentType);
}
}
- @SuppressWarnings("unchecked")
private Class> resolveCollectionElementType() {
- if (this.field != null) {
- return GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.fieldNestingLevel);
- }
- else if (this.methodParameter != null) {
+ if (this.methodParameter != null) {
return GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter);
}
- else if (this.value instanceof Collection) {
- Class> elementType = CollectionUtils.findCommonElementType((Collection) this.value);
- if (elementType != null) {
- return elementType;
- }
+ else if (this.field != null) {
+ return GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.fieldNestingLevel);
}
- else if (this.type != null) {
+ else {
return GenericCollectionTypeResolver.getCollectionType((Class extends Collection>) this.type);
- }
- return null;
+ }
}
-
- @SuppressWarnings("unchecked")
+
private Class> resolveMapKeyType() {
- if (this.field != null) {
- return GenericCollectionTypeResolver.getMapKeyFieldType(this.field);
- }
- else if (this.methodParameter != null) {
+ if (this.methodParameter != null) {
return GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter);
}
- else if (this.value instanceof Map, ?>) {
- Class> keyType = CollectionUtils.findCommonElementType(((Map, ?>) this.value).keySet());
- if (keyType != null) {
- return keyType;
- }
+ else if (this.field != null) {
+ return GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.fieldNestingLevel);
}
- else if (this.type != null && isMap()) {
+ else {
return GenericCollectionTypeResolver.getMapKeyType((Class extends Map>) this.type);
}
- return null;
}
- @SuppressWarnings("unchecked")
private Class> resolveMapValueType() {
- if (this.field != null) {
- return GenericCollectionTypeResolver.getMapValueFieldType(this.field);
- }
- else if (this.methodParameter != null) {
+ if (this.methodParameter != null) {
return GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter);
}
- else if (this.value instanceof Map, ?>) {
- Class> valueType = CollectionUtils.findCommonElementType(((Map, ?>) this.value).values());
- if (valueType != null) {
- return valueType;
- }
+ else if (this.field != null) {
+ return GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.fieldNestingLevel);
}
- else if (this.type != null && isMap()) {
+ else {
return GenericCollectionTypeResolver.getMapValueType((Class extends Map>) this.type);
}
- return null;
}
- private Annotation[] resolveAnnotations() {
- if (this.field != null) {
- return this.field.getAnnotations();
- }
- else if (this.methodParameter != null) {
- if (this.methodParameter.getParameterIndex() < 0) {
- return this.methodParameter.getMethodAnnotations();
- }
- else {
- return this.methodParameter.getParameterAnnotations();
- }
- }
- else {
- return EMPTY_ANNOTATION_ARRAY;
- }
+ // internal constructors
+
+ private TypeDescriptor(Class> componentType, Field field, int nestingLevel) {
+ this.type = componentType;
+ this.field = field;
+ this.fieldNestingLevel = nestingLevel;
}
+ private TypeDescriptor() {
+ }
- // static factory methods
+ private TypeDescriptor(Class> type) {
+ Assert.notNull(type, "Type must not be null");
+ this.type = type;
+ }
- /**
- * Create a new type descriptor for the class of the given object.
- * @param object the object
- * @return the type descriptor
- */
- public static TypeDescriptor forObject(Object object) {
- if (object == null) {
- return NULL;
- }
- else if (object instanceof Collection> || object instanceof Map, ?>) {
- return new TypeDescriptor(object);
- }
- else {
- return valueOf(object.getClass());
+ private TypeDescriptor(Class> collectionType, Class> elementType) {
+ this.type = collectionType;
+ if (elementType == null) {
+ elementType = Object.class;
}
+ this.elementType = TypeDescriptor.valueOf(elementType);
}
-
- /**
- * Create a new type descriptor for the given class.
- * @param type the class
- * @return the type descriptor
- */
- public static TypeDescriptor valueOf(Class> type) {
- if (type == null) {
- return TypeDescriptor.NULL;
+
+ private TypeDescriptor(Class> mapType, Class> keyType, Class> valueType) {
+ this.type = mapType;
+ if (keyType == null) {
+ keyType = Object.class;
}
- TypeDescriptor desc = typeDescriptorCache.get(type);
- return (desc != null ? desc : new TypeDescriptor(type));
+ if (valueType == null) {
+ valueType = Object.class;
+ }
+ this.mapKeyType = TypeDescriptor.valueOf(keyType);
+ this.mapValueType = TypeDescriptor.valueOf(valueType);
}
-}
+}
\ No newline at end of file
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionConverter.java
index 5608a4ed575..5c0b5c96e48 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionConverter.java
@@ -52,7 +52,6 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter {
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
}
- @SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
@@ -61,7 +60,7 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter {
Collection target = CollectionFactory.createCollection(targetType.getType(), length);
for (int i = 0; i < length; i++) {
Object sourceElement = Array.get(source, i);
- Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(sourceElement));
+ Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
target.add(targetElement);
}
return target;
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToArrayConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToArrayConverter.java
index e4d70903e39..e26bae837f7 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToArrayConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToArrayConverter.java
@@ -60,7 +60,7 @@ final class CollectionToArrayConverter implements ConditionalGenericConverter {
Object array = Array.newInstance(targetType.getElementType(), sourceCollection.size());
int i = 0;
for (Object sourceElement : sourceCollection) {
- Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(sourceElement), targetType.getElementTypeDescriptor());
+ Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
Array.set(array, i++, targetElement);
}
return array;
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionConverter.java
index 906bc423e90..4b573b10517 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionConverter.java
@@ -52,7 +52,6 @@ final class CollectionToCollectionConverter implements ConditionalGenericConvert
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
}
- @SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
@@ -63,9 +62,7 @@ final class CollectionToCollectionConverter implements ConditionalGenericConvert
}
Collection target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size());
for (Object sourceElement : sourceCollection) {
- Object targetElement = this.conversionService.convert(sourceElement,
- sourceType.getElementTypeDescriptor(sourceElement),
- targetType.getElementTypeDescriptor(sourceElement));
+ Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
target.add(targetElement);
}
return target;
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToObjectConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToObjectConverter.java
index 9f1ac3b3ce2..c97b4c9f685 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToObjectConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToObjectConverter.java
@@ -55,7 +55,7 @@ final class CollectionToObjectConverter implements ConditionalGenericConverter {
return null;
}
Object firstElement = sourceCollection.iterator().next();
- return this.conversionService.convert(firstElement, sourceType.getElementTypeDescriptor(firstElement), targetType);
+ return this.conversionService.convert(firstElement, sourceType.getElementTypeDescriptor(), targetType);
}
}
\ No newline at end of file
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToStringConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToStringConverter.java
index d5ca3c8da94..39fc0c4debf 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToStringConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToStringConverter.java
@@ -62,8 +62,7 @@ final class CollectionToStringConverter implements ConditionalGenericConverter {
if (i > 0) {
string.append(DELIMITER);
}
- Object targetElement = this.conversionService.convert(
- sourceElement, sourceType.getElementTypeDescriptor(sourceElement), targetType);
+ Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType);
string.append(targetElement);
i++;
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/MapToMapConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/MapToMapConverter.java
index 172bda410a3..9a099773c6e 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/MapToMapConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/MapToMapConverter.java
@@ -67,12 +67,8 @@ final class MapToMapConverter implements ConditionalGenericConverter {
Map.Entry sourceMapEntry = (Map.Entry) entry;
Object sourceKey = sourceMapEntry.getKey();
Object sourceValue = sourceMapEntry.getValue();
- Object targetKey = this.conversionService.convert(sourceKey,
- sourceType.getMapKeyTypeDescriptor(sourceKey),
- targetType.getMapKeyTypeDescriptor(sourceKey));
- Object targetValue = this.conversionService.convert(sourceValue,
- sourceType.getMapValueTypeDescriptor(sourceValue),
- targetType.getMapValueTypeDescriptor(sourceValue));
+ Object targetKey = this.conversionService.convert(sourceKey, sourceType.getMapKeyTypeDescriptor(), targetType.getMapKeyTypeDescriptor());
+ Object targetValue = this.conversionService.convert(sourceValue, sourceType.getMapValueTypeDescriptor(), targetType.getMapValueTypeDescriptor());
targetMap.put(targetKey, targetValue);
}
return targetMap;
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionConverter.java
index 6e9bdd94bc7..79204683bec 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionConverter.java
@@ -49,13 +49,12 @@ final class ObjectToCollectionConverter implements ConditionalGenericConverter {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
}
- @SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Collection target = CollectionFactory.createCollection(targetType.getType(), 1);
- TypeDescriptor elementType = targetType.getElementTypeDescriptor(source);
+ TypeDescriptor elementType = targetType.getElementTypeDescriptor();
// Avoid potential recursion...
if (!Collection.class.isAssignableFrom(elementType.getType())) {
target.add(this.conversionService.convert(source, sourceType, elementType));
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/PropertyTypeDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/PropertyTypeDescriptor.java
index d6b6b07ceaf..dff40931476 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/PropertyTypeDescriptor.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/PropertyTypeDescriptor.java
@@ -40,31 +40,21 @@ public class PropertyTypeDescriptor extends TypeDescriptor {
private final PropertyDescriptor propertyDescriptor;
- private Annotation[] cachedAnnotations;
-
-
/**
* Create a new BeanTypeDescriptor for the given bean property.
* @param propertyDescriptor the corresponding JavaBean PropertyDescriptor
* @param methodParameter the target method parameter
*/
- public PropertyTypeDescriptor(PropertyDescriptor propertyDescriptor, MethodParameter methodParameter) {
+ public PropertyTypeDescriptor(MethodParameter methodParameter, PropertyDescriptor propertyDescriptor) {
super(methodParameter);
this.propertyDescriptor = propertyDescriptor;
}
-
- /**
- * Create a new BeanTypeDescriptor for the given bean property.
- * @param propertyDescriptor the corresponding JavaBean PropertyDescriptor
- * @param methodParameter the target method parameter
- * @param type the specific type to expose (may be an array/collection element)
- */
- public PropertyTypeDescriptor(PropertyDescriptor propertyDescriptor, MethodParameter methodParameter, Class> type) {
- super(methodParameter, type);
+
+ public PropertyTypeDescriptor(Class> componentType, MethodParameter methodParameter, PropertyDescriptor propertyDescriptor) {
+ super(componentType, methodParameter);
this.propertyDescriptor = propertyDescriptor;
}
-
/**
* Return the underlying PropertyDescriptor.
*/
@@ -72,58 +62,48 @@ public class PropertyTypeDescriptor extends TypeDescriptor {
return this.propertyDescriptor;
}
- public Annotation[] getAnnotations() {
- Annotation[] anns = this.cachedAnnotations;
- if (anns == null) {
- Map, Annotation> annMap = new LinkedHashMap, Annotation>();
- String name = this.propertyDescriptor.getName();
- if (StringUtils.hasLength(name)) {
- Class> clazz = getMethodParameter().getMethod().getDeclaringClass();
- Field field = ReflectionUtils.findField(clazz, name);
+ protected Annotation[] resolveAnnotations() {
+ Map, Annotation> annMap = new LinkedHashMap, Annotation>();
+ String name = this.propertyDescriptor.getName();
+ if (StringUtils.hasLength(name)) {
+ Class> clazz = getMethodParameter().getMethod().getDeclaringClass();
+ Field field = ReflectionUtils.findField(clazz, name);
+ if (field == null) {
+ // Same lenient fallback checking as in CachedIntrospectionResults...
+ field = ReflectionUtils.findField(clazz, name.substring(0, 1).toLowerCase() + name.substring(1));
if (field == null) {
- // Same lenient fallback checking as in CachedIntrospectionResults...
- field = ReflectionUtils.findField(clazz, name.substring(0, 1).toLowerCase() + name.substring(1));
- if (field == null) {
- field = ReflectionUtils.findField(clazz, name.substring(0, 1).toUpperCase() + name.substring(1));
- }
- }
- if (field != null) {
- for (Annotation ann : field.getAnnotations()) {
- annMap.put(ann.annotationType(), ann);
- }
+ field = ReflectionUtils.findField(clazz, name.substring(0, 1).toUpperCase() + name.substring(1));
}
}
- Method writeMethod = this.propertyDescriptor.getWriteMethod();
- Method readMethod = this.propertyDescriptor.getReadMethod();
- if (writeMethod != null && writeMethod != getMethodParameter().getMethod()) {
- for (Annotation ann : writeMethod.getAnnotations()) {
+ if (field != null) {
+ for (Annotation ann : field.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
- if (readMethod != null && readMethod != getMethodParameter().getMethod()) {
- for (Annotation ann : readMethod.getAnnotations()) {
- annMap.put(ann.annotationType(), ann);
- }
- }
- for (Annotation ann : getMethodParameter().getMethodAnnotations()) {
+ }
+ Method writeMethod = this.propertyDescriptor.getWriteMethod();
+ Method readMethod = this.propertyDescriptor.getReadMethod();
+ if (writeMethod != null && writeMethod != getMethodParameter().getMethod()) {
+ for (Annotation ann : writeMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
- for (Annotation ann : getMethodParameter().getParameterAnnotations()) {
+ }
+ if (readMethod != null && readMethod != getMethodParameter().getMethod()) {
+ for (Annotation ann : readMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
- anns = annMap.values().toArray(new Annotation[annMap.size()]);
- this.cachedAnnotations = anns;
}
- return anns;
- }
-
- public TypeDescriptor forElementType(Class> elementType) {
- if (elementType != null) {
- return new PropertyTypeDescriptor(this.propertyDescriptor, getMethodParameter(), elementType);
+ for (Annotation ann : getMethodParameter().getMethodAnnotations()) {
+ annMap.put(ann.annotationType(), ann);
}
- else {
- return super.forElementType(null);
+ for (Annotation ann : getMethodParameter().getParameterAnnotations()) {
+ annMap.put(ann.annotationType(), ann);
}
+ return annMap.values().toArray(new Annotation[annMap.size()]);
+ }
+
+ public TypeDescriptor newComponentTypeDescriptor(Class> componentType, MethodParameter nested) {
+ return new PropertyTypeDescriptor(componentType, nested, this.propertyDescriptor);
}
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/StringToCollectionConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/StringToCollectionConverter.java
index 744caa0b6e8..767d495a025 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/StringToCollectionConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/StringToCollectionConverter.java
@@ -48,7 +48,6 @@ final class StringToCollectionConverter implements ConditionalGenericConverter {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
}
- @SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
@@ -57,8 +56,7 @@ final class StringToCollectionConverter implements ConditionalGenericConverter {
String[] fields = StringUtils.commaDelimitedListToStringArray(string);
Collection target = CollectionFactory.createCollection(targetType.getType(), fields.length);
for (String sourceElement : fields) {
- Object targetElement = this.conversionService.convert(sourceElement.trim(),
- sourceType, targetType.getElementTypeDescriptor(sourceElement));
+ Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementTypeDescriptor());
target.add(targetElement);
}
return target;
diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java
index 64f04e66df9..7c7eca223e3 100644
--- a/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java
+++ b/org.springframework.core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java
@@ -16,16 +16,17 @@
package org.springframework.core.convert;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
+import org.junit.Ignore;
import org.junit.Test;
/**
@@ -47,7 +48,8 @@ public class TypeDescriptorTests {
public Map mapField = new HashMap();
-
+ public Map> nestedMapField = new HashMap>();
+
@Test
public void listDescriptor() throws Exception {
TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("listOfString"));
@@ -94,14 +96,27 @@ public class TypeDescriptorTests {
}
@Test
+ @Ignore
public void complexTypeDescriptor() throws Exception {
TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("arrayOfListOfString"));
assertTrue(typeDescriptor.isArray());
assertEquals(List.class,typeDescriptor.getElementType());
+ assertEquals(String.class, typeDescriptor.getElementTypeDescriptor().getElementType());
+
// TODO asc notice that the type of the list elements is lost: typeDescriptor.getElementType() should return a TypeDescriptor
assertEquals("java.util.List[]",typeDescriptor.asString());
}
+ @Test
+ public void complexTypeDescriptor2() throws Exception {
+ TypeDescriptor typeDescriptor = new TypeDescriptor(TypeDescriptorTests.class.getDeclaredField("nestedMapField"));
+ assertTrue(typeDescriptor.isMap());
+ assertEquals(String.class,typeDescriptor.getMapKeyType());
+ assertEquals(List.class, typeDescriptor.getMapValueType());
+ assertEquals(Integer.class, typeDescriptor.getMapValueTypeDescriptor().getElementType());
+ assertEquals("java.util.Map>", typeDescriptor.asString());
+ }
+
@Test
public void testEquals() throws Exception {
TypeDescriptor t1 = TypeDescriptor.valueOf(String.class);
diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java
index 94bdb79b342..b87e8f39ec2 100644
--- a/org.springframework.core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java
+++ b/org.springframework.core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java
@@ -16,6 +16,13 @@
package org.springframework.core.convert.support;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.awt.Color;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.AbstractList;
@@ -33,13 +40,14 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
-import static org.junit.Assert.*;
import org.junit.Test;
-
+import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.convert.converter.ConverterRegistry;
/**
* @author Keith Donald
@@ -286,6 +294,24 @@ public class DefaultConversionTests {
assertEquals(new Integer("3"), result.get(2));
}
+ @Test
+ public void testSpr7766() throws Exception {
+ ConverterRegistry registry = ((ConverterRegistry) conversionService);
+ registry.addConverter(new ColorConverter());
+ List colors = (List) conversionService.convert(new String[] { "ffffff", "#000000" }, TypeDescriptor.valueOf(String[].class), new TypeDescriptor(new MethodParameter(getClass().getMethod("handlerMethod", List.class), 0)));
+ assertEquals(2, colors.size());
+ assertEquals(Color.WHITE, colors.get(0));
+ assertEquals(Color.BLACK, colors.get(1));
+ }
+
+ public class ColorConverter implements Converter {
+ public Color convert(String source) { if (!source.startsWith("#")) source = "#" + source; return Color.decode(source); }
+ }
+
+ public void handlerMethod(List color) {
+
+ }
+
@Test
public void convertArrayToCollectionImpl() {
LinkedList> result = conversionService.convert(new String[] { "1", "2", "3" }, LinkedList.class);
diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java b/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java
index f3bed382e64..df0dd25181a 100644
--- a/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java
+++ b/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java
@@ -29,7 +29,7 @@ import org.springframework.core.convert.TypeDescriptor;
*/
public class TypedValue {
- public static final TypedValue NULL = new TypedValue(null, TypeDescriptor.NULL);
+ public static final TypedValue NULL = new TypedValue(null);
private final Object value;
diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java b/org.springframework.expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java
index 0a591c6388d..b2f00b30cd9 100644
--- a/org.springframework.expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java
+++ b/org.springframework.expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java
@@ -44,7 +44,7 @@ public abstract class ExpressionUtils {
*/
public static T convert(EvaluationContext context, Object value, Class targetType) throws EvaluationException {
// TODO remove this function over time and use the one it delegates to
- return convertTypedValue(context,new TypedValue(value,TypeDescriptor.forObject(value)),targetType);
+ return convertTypedValue(context,new TypedValue(value),targetType);
}
/**
diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java
index d4c8a6a60a0..dc1a1025618 100644
--- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java
+++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java
@@ -124,7 +124,7 @@ public class ExpressionState {
return TypedValue.NULL;
}
else {
- return new TypedValue(value, TypeDescriptor.forObject(value));
+ return new TypedValue(value);
}
}
@@ -183,7 +183,7 @@ public class ExpressionState {
OperatorOverloader overloader = this.relatedContext.getOperatorOverloader();
if (overloader.overridesOperation(op, left, right)) {
Object returnValue = overloader.operate(op, left, right);
- return new TypedValue(returnValue,TypeDescriptor.forObject(returnValue));
+ return new TypedValue(returnValue);
}
else {
String leftType = (left==null?"null":left.getClass().getName());
diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java
index b877f28abb0..faedfdc2ade 100644
--- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java
+++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java
@@ -17,8 +17,8 @@
package org.springframework.expression.spel.ast;
import java.lang.reflect.Array;
-import java.util.List;
import java.util.ArrayList;
+import java.util.List;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
@@ -234,7 +234,6 @@ public class ConstructorReference extends SpelNodeImpl {
else {
componentType = arrayTypeCode.getType();
}
- TypeDescriptor td = TypeDescriptor.valueOf(componentType);
Object newArray;
if (!hasInitializer()) {
// Confirm all dimensions were specified (for example [3][][5] is missing the 2nd dimension)
@@ -313,7 +312,7 @@ public class ConstructorReference extends SpelNodeImpl {
throw new IllegalStateException(arrayTypeCode.name());
}
}
- return new TypedValue(newArray, td);
+ return new TypedValue(newArray);
}
private void populateReferenceTypeArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java
index 31b02615649..d18c8bbea64 100644
--- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java
+++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java
@@ -90,20 +90,10 @@ public class Indexer extends SpelNodeImpl {
// Indexing into a Map
if (targetObject instanceof Map) {
- if (targetObject == null) {
- // Current decision: attempt to index into null map == exception and does not just return null
- throw new SpelEvaluationException(getStartPosition(),SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
- }
Object possiblyConvertedKey = index;
- if (targetObjectTypeDescriptor.isMapEntryTypeKnown()) {
- possiblyConvertedKey = state.convertValue(index,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapKeyType()));
- }
+ possiblyConvertedKey = state.convertValue(index, targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
Object o = ((Map, ?>) targetObject).get(possiblyConvertedKey);
- if (targetObjectTypeDescriptor.isMapEntryTypeKnown()) {
- return new TypedValue(o, targetObjectTypeDescriptor.getMapValueTypeDescriptor());
- } else {
- return new TypedValue(o);
- }
+ return new TypedValue(o, targetObjectTypeDescriptor.getMapValueTypeDescriptor().applyType(o));
}
if (targetObject == null) {
@@ -125,7 +115,7 @@ public class Indexer extends SpelNodeImpl {
int pos = 0;
for (Object o : c) {
if (pos == idx) {
- return new TypedValue(o, targetObjectTypeDescriptor.getElementTypeDescriptor());
+ return new TypedValue(o, targetObjectTypeDescriptor.getElementTypeDescriptor().applyType(o));
}
pos++;
}
@@ -195,10 +185,8 @@ public class Indexer extends SpelNodeImpl {
Map map = (Map)targetObject;
Object possiblyConvertedKey = index;
Object possiblyConvertedValue = newValue;
- if (targetObjectTypeDescriptor.isMapEntryTypeKnown()) {
- possiblyConvertedKey = state.convertValue(index.getValue(),TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapKeyType()));
- possiblyConvertedValue = state.convertValue(newValue,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapValueType()));
- }
+ possiblyConvertedKey = state.convertValue(index.getValue(), targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
+ possiblyConvertedValue = state.convertValue(newValue, targetObjectTypeDescriptor.getMapValueTypeDescriptor());
map.put(possiblyConvertedKey,possiblyConvertedValue);
return;
}
diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java
index dbf4e941c4f..a5a818a3b27 100644
--- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java
+++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java
@@ -19,7 +19,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
@@ -72,8 +71,7 @@ public class InlineList extends SpelNodeImpl {
constantList.add(((InlineList) child).getConstantValue());
}
}
- this.constant = new TypedValue(Collections.unmodifiableList(constantList), TypeDescriptor
- .valueOf(List.class));
+ this.constant = new TypedValue(Collections.unmodifiableList(constantList));
}
}
@@ -87,7 +85,7 @@ public class InlineList extends SpelNodeImpl {
for (int c = 0; c < childcount; c++) {
returnValue.add(getChild(c).getValue(expressionState));
}
- return new TypedValue(returnValue, TypeDescriptor.valueOf(List.class));
+ return new TypedValue(returnValue);
}
}
diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java
index 75ae01c62cf..19bd86cce82 100644
--- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java
+++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java
@@ -16,7 +16,6 @@
package org.springframework.expression.spel.ast;
-import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
@@ -50,7 +49,7 @@ public class OpDivide extends Operator {
}
}
Object result = state.operate(Operation.DIVIDE, operandOne, operandTwo);
- return new TypedValue(result,TypeDescriptor.forObject(result));
+ return new TypedValue(result);
}
}
diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java
index 6dc633c3bc5..e9c47e5e0df 100644
--- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java
+++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java
@@ -68,14 +68,14 @@ public class Projection extends SpelNodeImpl {
List