moved applyIndexedObject internal, now invoked inside forObject static factory method

This commit is contained in:
Keith Donald 2011-05-23 01:08:18 +00:00
parent 4d6a5849f7
commit 79f9d1cfc6
16 changed files with 77 additions and 93 deletions

View File

@ -24,6 +24,7 @@ import org.springframework.core.NestedRuntimeException;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
@SuppressWarnings("serial")
public abstract class ConversionException extends NestedRuntimeException { public abstract class ConversionException extends NestedRuntimeException {
/** /**

View File

@ -25,6 +25,7 @@ import org.springframework.util.ObjectUtils;
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 3.0 * @since 3.0
*/ */
@SuppressWarnings("serial")
public final class ConversionFailedException extends ConversionException { public final class ConversionFailedException extends ConversionException {
private final TypeDescriptor sourceType; private final TypeDescriptor sourceType;

View File

@ -138,10 +138,11 @@ public class TypeDescriptor {
return NULL; return NULL;
} }
if (object instanceof Collection<?>) { if (object instanceof Collection<?>) {
return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType((Collection<?>) object)); return new TypeDescriptor(object.getClass(), findCommonElement((Collection<?>) object));
} }
else if (object instanceof Map<?, ?>) { else if (object instanceof Map<?, ?>) {
return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType(((Map<?, ?>) object).keySet()), CollectionUtils.findCommonElementType(((Map<?, ?>) object).values())); Map<?, ?> map = (Map<?, ?>) object;
return new TypeDescriptor(map.getClass(), findCommonElement(map.keySet()), findCommonElement(map.values()));
} }
else { else {
return valueOf(object.getClass()); return valueOf(object.getClass());
@ -342,37 +343,6 @@ public class TypeDescriptor {
return methodParameter; return methodParameter;
} }
/**
* Create a copy of this nested type descriptor and apply the specific type information from the indexed value, if necessary.
* Used to support collection and map indexing scenarios, where the indexer has a reference to the indexed type descriptor but needs to ensure its type actually represents the indexed object type.
* This is necessary to support type conversion during collection and map binding operations where generic information may not be available.
*/
public TypeDescriptor applyIndexedObject(Object object) {
if (object == null) {
return this;
}
if (isCollection() && Object.class.equals(getElementType())) {
Collection<?> collection = (Collection<?>) object;
if (collection.size() > 0) {
return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType((Collection<?>) object), methodParameter, field, fieldNestingLevel, annotations);
} else {
return this;
}
}
else if (isMap() && Object.class.equals(getMapKeyType()) && Object.class.equals(getMapValueType())) {
Map<?, ?> map = (Map<?, ?>) object;
if (map.size() > 0) {
return new TypeDescriptor(object.getClass(), CollectionUtils.findCommonElementType(((Map<?, ?>) object).keySet()),
CollectionUtils.findCommonElementType(((Map<?, ?>) object).values()), methodParameter, field, fieldNestingLevel, annotations);
} else {
return this;
}
}
else {
return this;
}
}
// extending Object // extending Object
public boolean equals(Object obj) { public boolean equals(Object obj) {
@ -545,6 +515,20 @@ public class TypeDescriptor {
return methodParameter; return methodParameter;
} }
private static Object findCommonElement(Collection<?> values) {
Object candidate = null;
for (Object value : values) {
if (value != null) {
if (candidate == null) {
candidate = value;
} else if (candidate.getClass() != value.getClass()) {
return null;
}
}
}
return candidate;
}
// internal constructors // internal constructors
private TypeDescriptor() { private TypeDescriptor() {
@ -555,46 +539,44 @@ public class TypeDescriptor {
this.type = type; this.type = type;
} }
private TypeDescriptor(Class<?> collectionType, Class<?> elementType) {
this.type = collectionType;
if (elementType == null) {
elementType = Object.class;
}
this.elementType = TypeDescriptor.valueOf(elementType);
}
private TypeDescriptor(Class<?> mapType, Class<?> keyType, Class<?> valueType) {
this.type = mapType;
if (keyType == null) {
keyType = Object.class;
}
if (valueType == null) {
valueType = Object.class;
}
this.mapKeyType = TypeDescriptor.valueOf(keyType);
this.mapValueType = TypeDescriptor.valueOf(valueType);
}
private TypeDescriptor(Class<?> collectionType, Class<?> elementType, MethodParameter methodParameter, Field field, int fieldNestingLevel, Annotation[] annotations) {
this(collectionType, elementType);
this.methodParameter = methodParameter;
this.field = field;
this.fieldNestingLevel = fieldNestingLevel;
this.annotations = annotations;
}
private TypeDescriptor(Class<?> mapType, Class<?> keyType, Class<?> valueType, MethodParameter methodParameter, Field field, int fieldNestingLevel, Annotation[] annotations) {
this(mapType, keyType, valueType);
this.methodParameter = methodParameter;
this.field = field;
this.fieldNestingLevel = fieldNestingLevel;
this.annotations = annotations;
}
private TypeDescriptor(Class<?> nestedType, Field field, int nestingLevel) { private TypeDescriptor(Class<?> nestedType, Field field, int nestingLevel) {
this.type = nestedType; this.type = nestedType;
this.field = field; this.field = field;
this.fieldNestingLevel = nestingLevel; this.fieldNestingLevel = nestingLevel;
} }
public TypeDescriptor(Class<?> mapType, Object commonKey, Object commonValue) {
this.type = mapType;
this.mapKeyType = applyIndexedObject(commonKey);
this.mapValueType = applyIndexedObject(commonValue);
}
public TypeDescriptor(Class<?> collectionType, Object commonElement) {
this.type = collectionType;
this.elementType = applyIndexedObject(commonElement);
}
private TypeDescriptor applyIndexedObject(Object object) {
if (object == null) {
return TypeDescriptor.valueOf(Object.class);
}
if (object instanceof Collection<?>) {
Collection<?> collection = (Collection<?>) object;
if (collection.size() == 0) {
return TypeDescriptor.valueOf(Object.class);
}
return new TypeDescriptor(object.getClass(), findCommonElement((Collection<?>) object));
}
else if (object instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) object;
if (map.size() == 0) {
return TypeDescriptor.valueOf(Object.class);
}
return new TypeDescriptor(object.getClass(), findCommonElement(map.keySet()), findCommonElement(map.values()));
}
else {
return TypeDescriptor.valueOf(object.getClass());
}
}
} }

View File

@ -52,12 +52,13 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter {
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor()); return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
} }
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
return null; return null;
} }
int length = Array.getLength(source); int length = Array.getLength(source);
Collection target = CollectionFactory.createCollection(targetType.getType(), length); Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), length);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
Object sourceElement = Array.get(source, i); Object sourceElement = Array.get(source, i);
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor()); Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());

View File

@ -64,7 +64,7 @@ final class CollectionToArrayConverter implements ConditionalGenericConverter {
Object array = Array.newInstance(targetType.getElementType(), sourceCollection.size()); Object array = Array.newInstance(targetType.getElementType(), sourceCollection.size());
int i = 0; int i = 0;
for (Object sourceElement : sourceCollection) { for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor().applyIndexedObject(sourceElement), targetType.getElementTypeDescriptor()); Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
Array.set(array, i++, targetElement); Array.set(array, i++, targetElement);
} }
return array; return array;

View File

@ -57,14 +57,15 @@ final class CollectionToCollectionConverter implements ConditionalGenericConvert
return this.conversionService.canConvert(sourceElementType, targetElementType); return this.conversionService.canConvert(sourceElementType, targetElementType);
} }
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
return null; return null;
} }
Collection<?> sourceCollection = (Collection<?>) source; Collection<?> sourceCollection = (Collection<?>) source;
Collection target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size()); Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size());
for (Object sourceElement : sourceCollection) { for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor().applyIndexedObject(sourceElement), targetType.getElementTypeDescriptor()); Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
target.add(targetElement); target.add(targetElement);
} }
return target; return target;

View File

@ -59,7 +59,7 @@ final class CollectionToObjectConverter implements ConditionalGenericConverter {
return null; return null;
} }
Object firstElement = sourceCollection.iterator().next(); Object firstElement = sourceCollection.iterator().next();
return this.conversionService.convert(firstElement, sourceType.getElementTypeDescriptor().applyIndexedObject(firstElement), targetType); return this.conversionService.convert(firstElement, sourceType.getElementTypeDescriptor(), targetType);
} }
} }

View File

@ -16,9 +16,6 @@
package org.springframework.core.convert.support; package org.springframework.core.convert.support;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.ConverterRegistry;
/** /**
* A specialization of {@link GenericConversionService} configured by default with * A specialization of {@link GenericConversionService} configured by default with
* converters appropriate for most applications. * converters appropriate for most applications.

View File

@ -481,10 +481,10 @@ public class GenericConversionService implements ConversionService, ConverterReg
private final ConvertiblePair typeInfo; private final ConvertiblePair typeInfo;
private final Converter converter; private final Converter<Object, Object> converter;
public ConverterAdapter(ConvertiblePair typeInfo, Converter<?, ?> converter) { public ConverterAdapter(ConvertiblePair typeInfo, Converter<?, ?> converter) {
this.converter = converter; this.converter = (Converter<Object, Object>) converter;
this.typeInfo = typeInfo; this.typeInfo = typeInfo;
} }
@ -511,10 +511,10 @@ public class GenericConversionService implements ConversionService, ConverterReg
private final ConvertiblePair typeInfo; private final ConvertiblePair typeInfo;
private final ConverterFactory converterFactory; private final ConverterFactory<Object, Object> converterFactory;
public ConverterFactoryAdapter(ConvertiblePair typeInfo, ConverterFactory<?, ?> converterFactory) { public ConverterFactoryAdapter(ConvertiblePair typeInfo, ConverterFactory<?, ?> converterFactory) {
this.converterFactory = converterFactory; this.converterFactory = (ConverterFactory<Object, Object>) converterFactory;
this.typeInfo = typeInfo; this.typeInfo = typeInfo;
} }

View File

@ -55,6 +55,7 @@ final class MapToMapConverter implements ConditionalGenericConverter {
TypeDescriptor targetValueType = targetType.getMapValueTypeDescriptor(); TypeDescriptor targetValueType = targetType.getMapValueTypeDescriptor();
if (Object.class.equals(sourceKeyType.getType()) && Object.class.equals(sourceValueType.getType()) if (Object.class.equals(sourceKeyType.getType()) && Object.class.equals(sourceValueType.getType())
|| Object.class.equals(targetKeyType.getType()) && Object.class.equals(targetValueType.getType())) { || Object.class.equals(targetKeyType.getType()) && Object.class.equals(targetValueType.getType())) {
// catches the empty map case
return true; return true;
} }
return this.conversionService.canConvert(sourceType.getMapKeyTypeDescriptor(), targetType.getMapKeyTypeDescriptor()) && return this.conversionService.canConvert(sourceType.getMapKeyTypeDescriptor(), targetType.getMapKeyTypeDescriptor()) &&
@ -78,8 +79,8 @@ final class MapToMapConverter implements ConditionalGenericConverter {
for (Map.Entry<Object, Object> entry : sourceMap.entrySet()) { for (Map.Entry<Object, Object> entry : sourceMap.entrySet()) {
Object sourceKey = entry.getKey(); Object sourceKey = entry.getKey();
Object sourceValue = entry.getValue(); Object sourceValue = entry.getValue();
Object targetKey = this.conversionService.convert(sourceKey, sourceType.getMapKeyTypeDescriptor().applyIndexedObject(sourceKey), targetType.getMapKeyTypeDescriptor()); Object targetKey = this.conversionService.convert(sourceKey, sourceType.getMapKeyTypeDescriptor(), targetType.getMapKeyTypeDescriptor());
Object targetValue = this.conversionService.convert(sourceValue, sourceType.getMapValueTypeDescriptor().applyIndexedObject(sourceValue), targetType.getMapValueTypeDescriptor()); Object targetValue = this.conversionService.convert(sourceValue, sourceType.getMapValueTypeDescriptor(), targetType.getMapValueTypeDescriptor());
targetMap.put(targetKey, targetValue); targetMap.put(targetKey, targetValue);
} }
} }

View File

@ -49,11 +49,12 @@ final class ObjectToCollectionConverter implements ConditionalGenericConverter {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor()); return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
} }
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
return null; return null;
} }
Collection target = CollectionFactory.createCollection(targetType.getType(), 1); Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), 1);
TypeDescriptor elementType = targetType.getElementTypeDescriptor(); TypeDescriptor elementType = targetType.getElementTypeDescriptor();
// Avoid potential recursion... // Avoid potential recursion...
if (!Collection.class.isAssignableFrom(elementType.getType())) { if (!Collection.class.isAssignableFrom(elementType.getType())) {

View File

@ -44,8 +44,8 @@ import org.springframework.util.ReflectionUtils;
final class ObjectToObjectConverter implements ConditionalGenericConverter { final class ObjectToObjectConverter implements ConditionalGenericConverter {
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
Class source = sourceType.getObjectType(); Class<?> source = sourceType.getObjectType();
Class target = targetType.getObjectType(); Class<?> target = targetType.getObjectType();
return (!source.equals(target) && hasValueOfMethodOrConstructor(target, source)); return (!source.equals(target) && hasValueOfMethodOrConstructor(target, source));
} }

View File

@ -20,11 +20,9 @@ import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;

View File

@ -48,13 +48,14 @@ final class StringToCollectionConverter implements ConditionalGenericConverter {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor()); return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
} }
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) { if (source == null) {
return null; return null;
} }
String string = (String) source; String string = (String) source;
String[] fields = StringUtils.commaDelimitedListToStringArray(string); String[] fields = StringUtils.commaDelimitedListToStringArray(string);
Collection target = CollectionFactory.createCollection(targetType.getType(), fields.length); Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), fields.length);
for (String sourceElement : fields) { for (String sourceElement : fields) {
Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementTypeDescriptor()); Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementTypeDescriptor());
target.add(targetElement); target.add(targetElement);

View File

@ -25,7 +25,7 @@ import org.springframework.core.convert.converter.ConverterFactory;
* @author Keith Donald * @author Keith Donald
* @since 3.0 * @since 3.0
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings({ "unchecked", "rawtypes" })
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> { final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) { public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {

View File

@ -93,7 +93,7 @@ public class Indexer extends SpelNodeImpl {
Object possiblyConvertedKey = index; Object possiblyConvertedKey = index;
possiblyConvertedKey = state.convertValue(index, targetObjectTypeDescriptor.getMapKeyTypeDescriptor()); possiblyConvertedKey = state.convertValue(index, targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
Object o = ((Map<?, ?>) targetObject).get(possiblyConvertedKey); Object o = ((Map<?, ?>) targetObject).get(possiblyConvertedKey);
return new TypedValue(o, targetObjectTypeDescriptor.getMapValueTypeDescriptor().applyIndexedObject(o)); return new TypedValue(o, targetObjectTypeDescriptor.getMapValueTypeDescriptor());
} }
if (targetObject == null) { if (targetObject == null) {
@ -105,7 +105,7 @@ public class Indexer extends SpelNodeImpl {
int idx = (Integer)state.convertValue(index, TypeDescriptor.valueOf(Integer.class)); int idx = (Integer)state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
if (targetObject.getClass().isArray()) { if (targetObject.getClass().isArray()) {
Object arrayElement = accessArrayElement(targetObject, idx); Object arrayElement = accessArrayElement(targetObject, idx);
return new TypedValue(arrayElement, targetObjectTypeDescriptor.getElementTypeDescriptor().applyIndexedObject(arrayElement)); return new TypedValue(arrayElement, targetObjectTypeDescriptor.getElementTypeDescriptor());
} else if (targetObject instanceof Collection) { } else if (targetObject instanceof Collection) {
Collection c = (Collection) targetObject; Collection c = (Collection) targetObject;
if (idx >= c.size()) { if (idx >= c.size()) {
@ -116,7 +116,7 @@ public class Indexer extends SpelNodeImpl {
int pos = 0; int pos = 0;
for (Object o : c) { for (Object o : c) {
if (pos == idx) { if (pos == idx) {
return new TypedValue(o, targetObjectTypeDescriptor.getElementTypeDescriptor().applyIndexedObject(o)); return new TypedValue(o, targetObjectTypeDescriptor.getElementTypeDescriptor());
} }
pos++; pos++;
} }