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 f284d284c64..1725ae68e2a 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
@@ -174,7 +174,7 @@ class TypeConverterDelegate {
else {
typeDesc = TypeDescriptor.valueOf(requiredType);
}
- if (conversionService.canConvert(convertedValue.getClass(), typeDesc)) {
+ if (conversionService.matches(convertedValue.getClass(), typeDesc)) {
return (T) conversionService.convert(convertedValue, typeDesc);
}
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionFailedException.java b/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionFailedException.java
index 1f9ef12b8b2..5846cfbade7 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionFailedException.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionFailedException.java
@@ -16,9 +16,6 @@
package org.springframework.core.convert;
-import org.springframework.core.style.StylerUtils;
-import org.springframework.util.ClassUtils;
-
/**
* Thrown when an attempt to execute a type conversion fails.
*
@@ -27,7 +24,7 @@ import org.springframework.util.ClassUtils;
*/
public class ConversionFailedException extends ConversionException {
- private Class> sourceType;
+ private TypeDescriptor sourceType;
private TypeDescriptor targetType;
@@ -38,7 +35,7 @@ public class ConversionFailedException extends ConversionException {
* @param targetType the value's target type
* @param cause the cause of the conversion failure
*/
- public ConversionFailedException(Class> sourceType, TypeDescriptor targetType, Object value, Throwable cause) {
+ public ConversionFailedException(TypeDescriptor sourceType, TypeDescriptor targetType, Object value, Throwable cause) {
super(buildDefaultMessage(value, sourceType, targetType, cause), cause);
this.sourceType = sourceType;
this.targetType = targetType;
@@ -47,7 +44,7 @@ public class ConversionFailedException extends ConversionException {
/**
* Return the source type we tried to convert the value from.
*/
- public Class> getSourceType() {
+ public TypeDescriptor getSourceType() {
return this.sourceType;
}
@@ -58,11 +55,10 @@ public class ConversionFailedException extends ConversionException {
return this.targetType;
}
-
- private static String buildDefaultMessage(Object value, Class> sourceType, TypeDescriptor targetType, Throwable cause) {
- return "Unable to convert value " + StylerUtils.style(value) + " from type '" +
- ClassUtils.getQualifiedName(sourceType) + "' to type '" +
- ClassUtils.getQualifiedName(targetType.getType()) + "'; reason = '" + cause.getMessage() + "'";
+ private static String buildDefaultMessage(Object value, TypeDescriptor sourceType, TypeDescriptor targetType,
+ Throwable cause) {
+ return "Unable to convert value " + value + " from type [" + sourceType.getName() + "] to type ["
+ + targetType.getName() + "]; reason = '" + cause.getMessage() + "'";
}
}
\ No newline at end of file
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java
index aebf448cf35..059858c724b 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java
@@ -18,10 +18,7 @@ package org.springframework.core.convert;
/**
* A service interface for type conversion. This is the entry point into the convert system.
- *
- *
Call {@link #convert(Object, Class)} to perform a thread-safe type conversion using this system.
- * Call {@link #convert(Object, TypeDescriptor)} to perform a conversion with additional context
- * about the targetType to convert to.
+ * Call {@link #convert(Object, Class)} to perform a thread-safe type conversion using this system.
*
* @author Keith Donald
* @since 3.0
@@ -35,7 +32,7 @@ public interface ConversionService {
* @return true if a conversion can be performed, false if not
*/
boolean canConvert(Class> sourceType, Class> targetType);
-
+
/**
* Convert the source to targetType.
* @param source the source to convert from (may be null)
@@ -46,24 +43,25 @@ public interface ConversionService {
T convert(Object source, Class targetType);
/**
- * Returns true if objects of sourceType can be converted to the targetType described by the TypeDescriptor.
- * The TypeDescriptor provides additional context about the point where conversion is needed, often an object property location.
+ * Returns true if objects of sourceType can be converted to the targetType.
+ * The TypeDescriptors provide additional context about the variable locations where conversion would occur, often object property locations.
* This flavor of the canConvert operation is mainly for use by a data binding framework, and not by user code.
- * @param source the source type to convert from (required)
+ * @param source context about the source type to convert from (required)
* @param targetType context about the target type to convert to (required)
- * @return true if a conversion can be performed, false if not
+ * @return true if a conversion can be performed between the source and target types, false if not
*/
- boolean canConvert(Class> sourceType, TypeDescriptor targetType);
+ boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
/**
- * Convert the source to the targetType described by the TypeDescriptor.
- * The TypeDescriptor provides additional context about the point where conversion is needed, often a object property location.
+ * Convert the source to targetTyp.
+ * The TypeDescriptors provide additional context about the variable locations where conversion will occur, often object property locations.
* This flavor of the convert operation is mainly for use by a data binding framework, and not by user code.
* @param source the source to convert from (may be null)
+ * @param sourceType context about the source type to convert from (required)
* @param targetType context about the target type to convert to (required)
- * @return the converted object, an instance of {@link TypeDescriptor#getType()}
+ * @return the converted object, an instance of {@link TypeDescriptor#getObjectType()}
* @throws ConversionException if an exception occurred
*/
- Object convert(Object source, TypeDescriptor targetType);
+ Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/ConverterNotFoundException.java b/org.springframework.core/src/main/java/org/springframework/core/convert/ConverterNotFoundException.java
index 53537457ea3..6e7bdb89edc 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/ConverterNotFoundException.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/ConverterNotFoundException.java
@@ -24,9 +24,9 @@ package org.springframework.core.convert;
*/
public class ConverterNotFoundException extends ConversionException {
- private final Class> sourceType;
+ private final TypeDescriptor sourceType;
- private final Class> targetType;
+ private final TypeDescriptor targetType;
/**
* Creates a new conversion executor not found exception.
@@ -34,7 +34,7 @@ public class ConverterNotFoundException extends ConversionException {
* @param targetType the target type requested to convert to
* @param message a descriptive message
*/
- public ConverterNotFoundException(Class> sourceType, Class> targetType) {
+ public ConverterNotFoundException(TypeDescriptor sourceType, TypeDescriptor targetType) {
super("No converter found capable of converting from [" + sourceType.getName() + "] to [" + targetType.getName() + "]");
this.sourceType = sourceType;
this.targetType = targetType;
@@ -43,14 +43,14 @@ public class ConverterNotFoundException extends ConversionException {
/**
* Returns the source type that was requested to convert from.
*/
- public Class> getSourceType() {
+ public TypeDescriptor getSourceType() {
return this.sourceType;
}
/**
* Returns the target type that was requested to convert to.
*/
- public Class> getTargetType() {
+ public TypeDescriptor getTargetType() {
return this.targetType;
}
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 ddaa6e4e4af..4a41eae0f87 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
@@ -178,6 +178,13 @@ public class TypeDescriptor {
}
}
+ /**
+ * Return the element type as a type descriptor.
+ */
+ public TypeDescriptor getElementTypeDescriptor() {
+ return TypeDescriptor.valueOf(getElementType());
+ }
+
/**
* Is this type a {@link Map} type?
*/
@@ -224,6 +231,14 @@ public class TypeDescriptor {
}
}
+ public TypeDescriptor getMapKeyTypeDescriptor() {
+ return TypeDescriptor.valueOf(getMapKeyType());
+ }
+
+ public TypeDescriptor getMapValueTypeDescriptor() {
+ return TypeDescriptor.valueOf(getMapValueType());
+ }
+
/**
* Obtain the annotations associated with the wrapped parameter/field, if any.
*/
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayGenericConverter.java
new file mode 100644
index 00000000000..fcac427afbd
--- /dev/null
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayGenericConverter.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2002-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.core.convert.support;
+
+import java.lang.reflect.Array;
+
+import org.springframework.core.convert.TypeDescriptor;
+
+/**
+ * A generic converter that can convert from one array type to another.
+ *
+ * @author Keith Donald
+ * @author Juergen Hoeller
+ * @since 3.0
+ */
+class ArrayGenericConverter implements GenericConverter {
+
+ private GenericConversionService conversionService;
+
+ public ArrayGenericConverter(GenericConversionService conversionService) {
+ this.conversionService = conversionService;
+ }
+
+ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
+ return sourceType.isArray() && targetType.isArray();
+ }
+
+ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
+ if (sourceType.isAssignableTo(targetType)) {
+ return source;
+ }
+ TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
+ TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
+ Object target = Array.newInstance(targetElementType.getType(), Array.getLength(source));
+ GenericConverter converter = conversionService.getConverter(sourceElementType, targetElementType);
+ for (int i = 0; i < Array.getLength(target); i++) {
+ Array.set(target, i, converter.convert(Array.get(source, i), sourceElementType, targetElementType));
+ }
+ return target;
+ }
+
+}
\ No newline at end of file
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionGenericConverter.java
similarity index 65%
rename from org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionGenericConverter.java
rename to org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionGenericConverter.java
index 4c8c686845a..aa9ffb4ff73 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionGenericConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionGenericConverter.java
@@ -28,38 +28,43 @@ import org.springframework.core.convert.TypeDescriptor;
* @author Juergen Hoeller
* @since 3.0
*/
-class CollectionToCollectionGenericConverter implements GenericConverter {
+class CollectionGenericConverter implements GenericConverter {
private GenericConversionService conversionService;
- public CollectionToCollectionGenericConverter(GenericConversionService conversionService) {
+ public CollectionGenericConverter(GenericConversionService conversionService) {
this.conversionService = conversionService;
}
+
+ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
+ return sourceType.isCollection() && targetType.isCollection();
+ }
- public Object convert(Object source, TypeDescriptor targetType) {
+ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Collection sourceCollection = (Collection) source;
- Class targetElementType = targetType.getElementType();
- if (targetElementType == null) {
+ TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
+ if (targetElementType == TypeDescriptor.NULL) {
return compatibleCollectionWithoutElementConversion(sourceCollection, targetType);
}
- Class sourceElementType = getElementType(sourceCollection);
- if (sourceElementType == null || targetElementType.isAssignableFrom(sourceElementType)) {
+ TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
+ if (sourceElementType == TypeDescriptor.NULL) {
+ sourceElementType = getElementType(sourceCollection);
+ }
+ if (sourceElementType == TypeDescriptor.NULL || sourceElementType.isAssignableTo(targetElementType)) {
return compatibleCollectionWithoutElementConversion(sourceCollection, targetType);
}
Collection targetCollection = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size());
- TypeDescriptor targetElementTypeDescriptor = TypeDescriptor.valueOf(targetElementType);
- GenericConverter elementConverter = conversionService.getConverter(sourceElementType,
- targetElementTypeDescriptor);
+ GenericConverter elementConverter = conversionService.getConverter(sourceElementType, targetElementType);
for (Object element : sourceCollection) {
- targetCollection.add(elementConverter.convert(element, targetElementTypeDescriptor));
+ targetCollection.add(elementConverter.convert(element, sourceElementType, targetElementType));
}
return targetCollection;
}
- private Class getElementType(Collection collection) {
+ private TypeDescriptor getElementType(Collection collection) {
for (Object element : collection) {
if (element != null) {
- return element.getClass();
+ return TypeDescriptor.valueOf(element.getClass());
}
}
return null;
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java
index 914b69fc8b4..c77876600a8 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java
@@ -20,9 +20,9 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -38,7 +38,6 @@ import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.ConverterInfo;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
/**
* Base implementation of a conversion service.
@@ -56,9 +55,12 @@ public class GenericConversionService implements ConversionService, ConverterReg
private final Map> sourceTypeConverters = new HashMap>();
+ private final Set matchableConverters = new LinkedHashSet();
+
public GenericConversionService() {
- addGenericConverter(Collection.class, Collection.class, new CollectionToCollectionGenericConverter(this));
- addGenericConverter(Map.class, Map.class, new MapToMapGenericConverter(this));
+ addGenericConverter(new CollectionGenericConverter(this));
+ addGenericConverter(new MapGenericConverter(this));
+ addGenericConverter(new ArrayGenericConverter(this));
}
/**
@@ -122,106 +124,81 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
public void removeConvertible(Class> sourceType, Class> targetType) {
- Map sourceMap = getSourceMap(sourceType);
- sourceMap.remove(targetType);
+ getSourceMap(sourceType).remove(targetType);
}
// implementing ConversionService
public boolean canConvert(Class> sourceType, Class> targetType) {
- return canConvert(sourceType, TypeDescriptor.valueOf(targetType));
+ return canConvert(TypeDescriptor.valueOf(sourceType), TypeDescriptor.valueOf(targetType));
+ }
+
+ public T convert(Object source, Class targetType) {
+ Assert.notNull(targetType, "The targetType to convert to is required");
+ return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
}
- public boolean canConvert(Class> sourceType, TypeDescriptor targetType) {
+ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(sourceType, "The sourceType to convert from is required");
Assert.notNull(targetType, "The targetType to convert to is required");
if (targetType == TypeDescriptor.NULL) {
return true;
}
- Class sourceClass = ClassUtils.resolvePrimitiveIfNecessary(sourceType);
- Class targetClass = targetType.getObjectType();
- return getConverter(sourceClass, targetType) != null || this.parent != null
- && this.parent.canConvert(sourceClass, targetClass);
+ return getConverter(sourceType, targetType) != null || this.parent != null
+ && this.parent.canConvert(sourceType, targetType);
}
- public T convert(Object source, Class targetType) {
- Assert.notNull(targetType, "The targetType to convert to is required");
- return (T) convert(source, TypeDescriptor.valueOf(targetType));
- }
-
- public Object convert(Object source, TypeDescriptor targetType) {
+ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
+ Assert.notNull(sourceType, "The source type to convert to is required");
Assert.notNull(targetType, "The targetType to convert to is required");
if (source == null) {
return null;
}
+ Assert.isTrue(sourceType != TypeDescriptor.NULL,
+ "The source TypeDescriptor must not be TypeDescriptor.NULL when source != null");
if (targetType == TypeDescriptor.NULL) {
return null;
}
- Class sourceType = ClassUtils.resolvePrimitiveIfNecessary(source.getClass());
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
try {
- return converter.convert(source, targetType);
+ return converter.convert(source, sourceType, targetType);
} catch (ConversionFailedException e) {
throw e;
} catch (Exception e) {
throw new ConversionFailedException(sourceType, targetType, source, e);
}
} else {
- if (targetType.isAssignableValue(source)) {
- return source;
+ if (this.parent != null) {
+ return this.parent.convert(source, sourceType, targetType);
} else {
- throw new ConverterNotFoundException(sourceType, targetType.getObjectType());
+ if (targetType.isAssignableValue(source)) {
+ return source;
+ } else {
+ throw new ConverterNotFoundException(sourceType, targetType);
+ }
}
}
}
// subclassing hooks
- protected GenericConverter getConverter(Class sourceType, TypeDescriptor targetType) {
- if (sourceType.isInterface()) {
- LinkedList classQueue = new LinkedList();
- classQueue.addFirst(sourceType);
- while (!classQueue.isEmpty()) {
- Class currentClass = classQueue.removeLast();
- Map converters = getConvertersForSource(currentClass);
- GenericConverter converter = getConverter(converters, targetType);
- if (converter != null) {
- return converter;
- }
- Class[] interfaces = currentClass.getInterfaces();
- for (Class ifc : interfaces) {
- classQueue.addFirst(ifc);
- }
- }
- Map objectConverters = getConvertersForSource(Object.class);
- return getConverter(objectConverters, targetType);
- } else {
- LinkedList classQueue = new LinkedList();
- classQueue.addFirst(sourceType);
- while (!classQueue.isEmpty()) {
- Class currentClass = classQueue.removeLast();
- Map converters = getConvertersForSource(currentClass);
- GenericConverter converter = getConverter(converters, targetType);
- if (converter != null) {
- return converter;
- }
- if (currentClass.getSuperclass() != null) {
- classQueue.addFirst(currentClass.getSuperclass());
- }
- Class[] interfaces = currentClass.getInterfaces();
- for (Class ifc : interfaces) {
- classQueue.addFirst(ifc);
+ protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
+ GenericConverter converter = matchConverterByClassPair(sourceType.getObjectType(), targetType.getObjectType());
+ if (converter == null) {
+ for (GenericConverter matchableConverter : this.matchableConverters) {
+ if (matchableConverter.canConvert(sourceType, targetType)) {
+ return matchableConverter;
}
}
- return null;
}
+ return converter;
}
// internal helpers
- private void addGenericConverter(Class sourceType, Class targetType, GenericConverter converter) {
- getSourceMap(sourceType).put(targetType, converter);
+ private void addGenericConverter(GenericConverter converter) {
+ this.matchableConverters.add(converter);
}
private List getRequiredTypeInfo(Object converter) {
@@ -275,6 +252,46 @@ public class GenericConversionService implements ConversionService, ConverterReg
return null;
}
+ private GenericConverter matchConverterByClassPair(Class sourceType, Class targetType) {
+ if (sourceType.isInterface()) {
+ LinkedList classQueue = new LinkedList();
+ classQueue.addFirst(sourceType);
+ while (!classQueue.isEmpty()) {
+ Class currentClass = classQueue.removeLast();
+ Map converters = getConvertersForSource(currentClass);
+ GenericConverter converter = getConverter(converters, targetType);
+ if (converter != null) {
+ return converter;
+ }
+ Class[] interfaces = currentClass.getInterfaces();
+ for (Class ifc : interfaces) {
+ classQueue.addFirst(ifc);
+ }
+ }
+ Map objectConverters = getConvertersForSource(Object.class);
+ return getConverter(objectConverters, targetType);
+ } else {
+ LinkedList classQueue = new LinkedList();
+ classQueue.addFirst(sourceType);
+ while (!classQueue.isEmpty()) {
+ Class currentClass = classQueue.removeLast();
+ Map converters = getConvertersForSource(currentClass);
+ GenericConverter converter = getConverter(converters, targetType);
+ if (converter != null) {
+ return converter;
+ }
+ if (currentClass.getSuperclass() != null) {
+ classQueue.addFirst(currentClass.getSuperclass());
+ }
+ Class[] interfaces = currentClass.getInterfaces();
+ for (Class ifc : interfaces) {
+ classQueue.addFirst(ifc);
+ }
+ }
+ return null;
+ }
+ }
+
private Map getSourceMap(Class sourceType) {
Map sourceMap = sourceTypeConverters.get(sourceType);
if (sourceMap == null) {
@@ -292,11 +309,10 @@ public class GenericConversionService implements ConversionService, ConverterReg
return converters;
}
- private GenericConverter getConverter(Map converters, TypeDescriptor targetType) {
- Class targetClass = targetType.getObjectType();
- if (targetClass.isInterface()) {
+ private GenericConverter getConverter(Map converters, Class targetType) {
+ if (targetType.isInterface()) {
LinkedList classQueue = new LinkedList();
- classQueue.addFirst(targetClass);
+ classQueue.addFirst(targetType);
while (!classQueue.isEmpty()) {
Class currentClass = classQueue.removeLast();
GenericConverter converter = converters.get(currentClass);
@@ -311,7 +327,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
return converters.get(Object.class);
} else {
LinkedList classQueue = new LinkedList();
- classQueue.addFirst(targetClass);
+ classQueue.addFirst(targetType);
while (!classQueue.isEmpty()) {
Class currentClass = classQueue.removeLast();
GenericConverter converter = converters.get(currentClass);
@@ -338,7 +354,11 @@ public class GenericConversionService implements ConversionService, ConverterReg
this.converter = converter;
}
- public Object convert(Object source, TypeDescriptor type) {
+ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
+ throw new UnsupportedOperationException("Should not be called");
+ }
+
+ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return converter.convert(source);
}
@@ -352,7 +372,11 @@ public class GenericConversionService implements ConversionService, ConverterReg
this.converterFactory = converterFactory;
}
- public Object convert(Object source, TypeDescriptor targetType) {
+ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
+ throw new UnsupportedOperationException("Should not be called");
+ }
+
+ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return converterFactory.getConverter(targetType.getObjectType()).convert(source);
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConverter.java
index f17cef46d86..d21c875a1ba 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConverter.java
@@ -21,9 +21,9 @@ import org.springframework.core.convert.converter.ConverterFactory;
/**
* Uniform Converter interface used by GenericConversionService.
- * This interface is an internal detail of the GenericConversionService implementation.
+ * This interface is primarily an internal detail of the GenericConversionService implementation.
* It should generally not be implemented by application code directly.
- * See {@link Converter} and {@link ConverterFactory} interfaces for public converter SPIs.
+ * See {@link Converter} and {@link ConverterFactory} interfaces for simpler public converter SPIs.
* @author Keith Donald
* @since 3.0
* @see Converter
@@ -31,12 +31,21 @@ import org.springframework.core.convert.converter.ConverterFactory;
*/
public interface GenericConverter {
+ /**
+ * Can this converter convert the sourceType to targetType.
+ * @param sourceType context about the sourceType to convert to
+ * @param targetType context about the targetType to convert to
+ * @return true if so, false otherwise
+ */
+ boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
+
/**
* Convert the source to the targetType described by the TypeDescriptor.
* @param source the source object to convert (never null)
- * @param targetType the target type to convert to
+ * @param sourceType context about the source type to convert from
+ * @param targetType context about the target type to convert to
* @return the converted object
*/
- Object convert(Object source, TypeDescriptor targetType);
+ Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/MapToMapGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/MapGenericConverter.java
similarity index 57%
rename from org.springframework.core/src/main/java/org/springframework/core/convert/support/MapToMapGenericConverter.java
rename to org.springframework.core/src/main/java/org/springframework/core/convert/support/MapGenericConverter.java
index 4eb57b8979d..1141ddf884d 100644
--- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/MapToMapGenericConverter.java
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/MapGenericConverter.java
@@ -5,33 +5,37 @@ import java.util.Map;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.TypeDescriptor;
-class MapToMapGenericConverter implements GenericConverter {
+class MapGenericConverter implements GenericConverter {
private GenericConversionService conversionService;
- public MapToMapGenericConverter(GenericConversionService conversionService) {
+ public MapGenericConverter(GenericConversionService conversionService) {
this.conversionService = conversionService;
}
- public Object convert(Object source, TypeDescriptor targetType) {
+ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
+ return sourceType.isMap() && targetType.isMap();
+ }
+
+ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Map sourceMap = (Map) source;
- Class targetKeyType = targetType.getMapKeyType();
- Class targetValueType = targetType.getMapValueType();
+ TypeDescriptor targetKeyType = targetType.getMapKeyTypeDescriptor();
+ TypeDescriptor targetValueType = targetType.getMapValueTypeDescriptor();
if (targetKeyType == null && targetValueType == null) {
return compatibleMapWithoutEntryConversion(sourceMap, targetType);
}
- Class[] sourceEntryTypes = getMapEntryTypes(sourceMap);
- Class sourceKeyType = sourceEntryTypes[0];
- Class sourceValueType = sourceEntryTypes[1];
+ TypeDescriptor[] sourceEntryTypes = getMapEntryTypes(sourceMap);
+ TypeDescriptor sourceKeyType = sourceEntryTypes[0];
+ TypeDescriptor sourceValueType = sourceEntryTypes[1];
if (sourceKeyType == null && sourceValueType == null) {
return compatibleMapWithoutEntryConversion(sourceMap, targetType);
}
boolean keysCompatible = false;
- if (targetKeyType != null && sourceKeyType != null && targetKeyType.isAssignableFrom(sourceKeyType)) {
+ if (sourceKeyType != null && targetKeyType != null && sourceKeyType.isAssignableTo(targetKeyType)) {
keysCompatible = true;
}
boolean valuesCompatible = false;
- if (targetValueType != null && sourceValueType != null && targetValueType.isAssignableFrom(sourceValueType)) {
+ if (sourceValueType != null && targetValueType != null && sourceValueType.isAssignableTo(targetValueType)) {
valuesCompatible = true;
}
if (keysCompatible && valuesCompatible) {
@@ -46,7 +50,7 @@ class MapToMapGenericConverter implements GenericConverter {
return targetMap;
}
- private Class[] getMapEntryTypes(Map sourceMap) {
+ private TypeDescriptor[] getMapEntryTypes(Map sourceMap) {
Class keyType = null;
Class valueType = null;
for (Object entry : sourceMap.entrySet()) {
@@ -63,7 +67,7 @@ class MapToMapGenericConverter implements GenericConverter {
break;
}
}
- return new Class[] { keyType, valueType };
+ return new TypeDescriptor[] { TypeDescriptor.valueOf(keyType), TypeDescriptor.valueOf(valueType) };
}
private Map compatibleMapWithoutEntryConversion(Map source, TypeDescriptor targetType) {
@@ -82,26 +86,32 @@ class MapToMapGenericConverter implements GenericConverter {
private GenericConverter valueConverter;
- private TypeDescriptor targetKeyTypeDescriptor;
+ private TypeDescriptor sourceKeyType;
- private TypeDescriptor targetValueTypeDescriptor;
+ private TypeDescriptor sourceValueType;
+
+ private TypeDescriptor targetKeyType;
+
+ private TypeDescriptor targetValueType;
- public MapEntryConverter(Class sourceKeyType, Class sourceValueType, Class targetKeyType,
- Class targetValueType, boolean keysCompatible, boolean valuesCompatible,
+ public MapEntryConverter(TypeDescriptor sourceKeyType, TypeDescriptor sourceValueType, TypeDescriptor targetKeyType,
+ TypeDescriptor targetValueType, boolean keysCompatible, boolean valuesCompatible,
GenericConversionService conversionService) {
- if (sourceKeyType != null && targetKeyType != null && !keysCompatible) {
- this.targetKeyTypeDescriptor = TypeDescriptor.valueOf(targetKeyType);
- this.keyConverter = conversionService.getConverter(sourceKeyType, targetKeyTypeDescriptor);
+ if (sourceKeyType != TypeDescriptor.NULL && targetKeyType != TypeDescriptor.NULL && !keysCompatible) {
+ this.keyConverter = conversionService.getConverter(sourceKeyType, targetKeyType);
+ this.sourceKeyType = sourceKeyType;
+ this.targetKeyType = targetKeyType;
}
- if (sourceValueType != null && targetValueType != null && !valuesCompatible) {
- this.targetValueTypeDescriptor = TypeDescriptor.valueOf(targetValueType);
- this.valueConverter = conversionService.getConverter(sourceValueType, targetValueTypeDescriptor);
+ if (sourceValueType != TypeDescriptor.NULL && targetValueType != TypeDescriptor.NULL && !valuesCompatible) {
+ this.valueConverter = conversionService.getConverter(sourceValueType, targetValueType);
+ this.targetKeyType = targetKeyType;
+ this.targetValueType = targetValueType;
}
}
public Object convertKey(Object sourceKey) {
if (sourceKey != null && this.keyConverter != null) {
- return this.keyConverter.convert(sourceKey, targetKeyTypeDescriptor);
+ return this.keyConverter.convert(sourceKey, sourceKeyType, targetKeyType);
} else {
return sourceKey;
}
@@ -109,7 +119,7 @@ class MapToMapGenericConverter implements GenericConverter {
public Object convertValue(Object sourceValue) {
if (sourceValue != null && this.valueConverter != null) {
- return this.valueConverter.convert(sourceValue, targetValueTypeDescriptor);
+ return this.valueConverter.convert(sourceValue, sourceValueType, targetValueType);
} else {
return sourceValue;
}
diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/MatchableGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/MatchableGenericConverter.java
new file mode 100644
index 00000000000..3898392541a
--- /dev/null
+++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/MatchableGenericConverter.java
@@ -0,0 +1,9 @@
+package org.springframework.core.convert.support;
+
+import org.springframework.core.convert.TypeDescriptor;
+
+public interface MatchableGenericConverter extends GenericConverter {
+
+ boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
+
+}
diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java
index 6c840a2c078..59685165ddd 100644
--- a/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java
+++ b/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java
@@ -53,7 +53,7 @@ public class GenericConversionServiceTests {
public void executeConversionNullSource() {
assertEquals(null, converter.convert(null, Integer.class));
}
-
+
@Test
public void executeCompatibleSource() {
assertEquals(Boolean.FALSE, converter.convert(false, boolean.class));
@@ -86,18 +86,17 @@ public class GenericConversionServiceTests {
public void convertNull() {
assertNull(converter.convert(null, Integer.class));
}
-
- @Test(expected=IllegalArgumentException.class)
+
+ @Test(expected = IllegalArgumentException.class)
public void convertNullTargetClass() {
- assertEquals("3", converter.convert("3", (Class>)null));
+ assertEquals("3", converter.convert("3", (Class>) null));
}
@Test
public void convertNullConversionPointType() {
- assertEquals(null, converter.convert("3", TypeDescriptor.NULL));
+ assertEquals(null, converter.convert(3, TypeDescriptor.valueOf(String.class), TypeDescriptor.NULL));
}
-
@Test
public void convertWrongTypeArgument() {
converter.addConverterFactory(new StringToNumberConverterFactory());
@@ -128,7 +127,6 @@ public class GenericConversionServiceTests {
}
@Test
- @Ignore
public void convertArrayToArray() {
converter.addConverterFactory(new StringToNumberConverterFactory());
Integer[] result = converter.convert(new String[] { "1", "2", "3" }, Integer[].class);
@@ -138,7 +136,6 @@ public class GenericConversionServiceTests {
}
@Test
- @Ignore
public void convertArrayToPrimitiveArray() {
converter.addConverterFactory(new StringToNumberConverterFactory());
int[] result = converter.convert(new String[] { "1", "2", "3" }, int[].class);
@@ -146,6 +143,14 @@ public class GenericConversionServiceTests {
assertEquals(2, result[1]);
assertEquals(3, result[2]);
}
+
+ @Test
+ public void convertArrayToArrayAssignable() {
+ int[] result = converter.convert(new int[] { 1, 2, 3 }, int[].class);
+ assertEquals(1, result[0]);
+ assertEquals(2, result[1]);
+ assertEquals(3, result[2]);
+ }
@Test
@Ignore
@@ -162,7 +167,8 @@ public class GenericConversionServiceTests {
@Ignore
public void convertArrayToListGenericTypeConversion() throws Exception {
converter.addConverterFactory(new StringToNumberConverterFactory());
- List result = (List) converter.convert(new String[] { "1", "2", "3" }, new TypeDescriptor(getClass().getDeclaredField("genericList")));
+ List result = (List) converter.convert(new String[] { "1", "2", "3" }, TypeDescriptor
+ .valueOf(String[].class), new TypeDescriptor(getClass().getDeclaredField("genericList")));
assertEquals(new Integer("1"), result.get(0));
assertEquals(new Integer("2"), result.get(1));
assertEquals(new Integer("3"), result.get(2));
@@ -221,12 +227,13 @@ public class GenericConversionServiceTests {
foo.add("1");
foo.add("2");
foo.add("3");
- List bar = (List)converter.convert(foo, new TypeDescriptor(getClass().getField("genericList")));
+ List bar = (List) converter.convert(foo, TypeDescriptor.valueOf(List.class),
+ new TypeDescriptor(getClass().getField("genericList")));
assertEquals(new Integer(1), bar.get(0));
assertEquals(new Integer(2), bar.get(1));
assertEquals(new Integer(3), bar.get(2));
}
-
+
public Map genericMap = new HashMap();
@Test
@@ -236,7 +243,8 @@ public class GenericConversionServiceTests {
foo.put("2", "BAZ");
converter.addConverterFactory(new StringToNumberConverterFactory());
converter.addConverterFactory(new StringToEnumConverterFactory());
- Map map = (Map) converter.convert(foo, new TypeDescriptor(getClass().getField("genericMap")));
+ Map map = (Map) converter.convert(foo, TypeDescriptor.valueOf(Map.class),
+ new TypeDescriptor(getClass().getField("genericMap")));
assertEquals(map.get(1), FooEnum.BAR);
assertEquals(map.get(2), FooEnum.BAZ);
}
@@ -250,11 +258,11 @@ public class GenericConversionServiceTests {
assertEquals("2", result[1]);
assertEquals("3", result[2]);
}
-
+
@Test
@Ignore
public void convertStringToArrayWithElementConversion() {
- converter.addConverterFactory(new StringToNumberConverterFactory());
+ converter.addConverterFactory(new StringToNumberConverterFactory());
Integer[] result = converter.convert("1,2,3", Integer[].class);
assertEquals(3, result.length);
assertEquals(new Integer(1), result[0]);
@@ -262,7 +270,6 @@ public class GenericConversionServiceTests {
assertEquals(new Integer(3), result[2]);
}
-
public static enum FooEnum {
BAR, BAZ
}
diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java
index 5602841d965..f201bf18653 100644
--- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java
+++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java
@@ -38,26 +38,24 @@ import org.springframework.util.Assert;
*/
public class StandardTypeConverter implements TypeConverter {
- private final ConversionService typeConverter;
-
+ private final ConversionService conversionService;
public StandardTypeConverter() {
- this.typeConverter = new DefaultConversionService();
+ this.conversionService = new DefaultConversionService();
}
public StandardTypeConverter(ConversionService typeConverter) {
Assert.notNull(typeConverter, "ConversionService must not be null");
- this.typeConverter = typeConverter;
+ this.conversionService = typeConverter;
}
-
public boolean canConvert(Class> sourceType, Class> targetType) {
- return this.typeConverter.canConvert(sourceType, targetType);
+ return this.conversionService.canConvert(sourceType, targetType);
}
public Object convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException {
try {
- return this.typeConverter.convert(value, typeDescriptor);
+ return this.conversionService.convert(value, TypeDescriptor.forObject(value), typeDescriptor);
}
catch (ConverterNotFoundException cenfe) {
throw new SpelEvaluationException(cenfe, SpelMessage.TYPE_CONVERSION_ERROR, value.getClass(), typeDescriptor.asString());
diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestsUsingCoreConversionService.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestsUsingCoreConversionService.java
index b136d45642b..cbed2bd8681 100644
--- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestsUsingCoreConversionService.java
+++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestsUsingCoreConversionService.java
@@ -101,21 +101,11 @@ public class ExpressionTestsUsingCoreConversionService extends ExpressionTestCas
private final DefaultConversionService service = new DefaultConversionService();
public boolean canConvert(Class> sourceType, Class> targetType) {
- return this.service.canConvert(sourceType, TypeDescriptor.valueOf(targetType));
+ return this.service.canConvert(sourceType, targetType);
}
- public boolean canConvert(Class> sourceType, TypeDescriptor typeDescriptor) {
- return this.service.canConvert(sourceType, typeDescriptor);
- }
-
- @SuppressWarnings("cast")
- public T convertValue(Object value, Class targetType) throws EvaluationException {
- return (T) this.service.convert(value,TypeDescriptor.valueOf(targetType));
- }
-
- @SuppressWarnings("unchecked")
public Object convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException {
- return this.service.convert(value, typeDescriptor);
+ return this.service.convert(value, TypeDescriptor.forObject(value), typeDescriptor);
}
}