diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToArrayGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToArrayGenericConverter.java index a272cfb725d..2554f8b3691 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToArrayGenericConverter.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToArrayGenericConverter.java @@ -17,6 +17,7 @@ package org.springframework.core.convert.support; import java.lang.reflect.Array; +import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; class ArrayToArrayGenericConverter implements GenericConverter { @@ -34,7 +35,10 @@ class ArrayToArrayGenericConverter implements GenericConverter { TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor(); TypeDescriptor targetElementType = targetType.getElementTypeDescriptor(); Object target = Array.newInstance(targetElementType.getType(), Array.getLength(source)); - GenericConverter converter = conversionService.getConverter(sourceElementType, targetElementType); + GenericConverter converter = conversionService.getConverter(sourceElementType, targetElementType); + if (converter == null) { + throw new ConverterNotFoundException(sourceType, targetType); + } for (int i = 0; i < Array.getLength(target); i++) { Array.set(target, i, converter.convert(Array.get(source, i), sourceElementType, targetElementType)); } diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionGenericConverter.java index c5862eb5d2a..31edf0dfb89 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionGenericConverter.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionGenericConverter.java @@ -19,6 +19,7 @@ import java.lang.reflect.Array; import java.util.Collection; import org.springframework.core.CollectionFactory; +import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; class ArrayToCollectionGenericConverter implements GenericConverter { @@ -40,6 +41,9 @@ class ArrayToCollectionGenericConverter implements GenericConverter { } } else { GenericConverter converter = conversionService.getConverter(sourceElementType, targetElementType); + if (converter == null) { + throw new ConverterNotFoundException(sourceType, targetType); + } for (int i = 0; i < length; i++) { collection.add(converter.convert(Array.get(source, i), sourceElementType, targetElementType)); } diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToObjectGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToObjectGenericConverter.java index dde7f928ac6..89682456f90 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToObjectGenericConverter.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ArrayToObjectGenericConverter.java @@ -17,6 +17,7 @@ package org.springframework.core.convert.support; import java.lang.reflect.Array; +import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; class ArrayToObjectGenericConverter implements GenericConverter { @@ -37,6 +38,9 @@ class ArrayToObjectGenericConverter implements GenericConverter { return Array.get(source, 0); } else { GenericConverter converter = conversionService.getConverter(sourceElementType, targetType); + if (converter == null) { + throw new ConverterNotFoundException(sourceType, targetType); + } return converter.convert(Array.get(source, 0), sourceElementType, targetType); } } diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToArrayGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToArrayGenericConverter.java index 7b8816a7452..5560a99bc23 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToArrayGenericConverter.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToArrayGenericConverter.java @@ -19,6 +19,7 @@ import java.lang.reflect.Array; import java.util.Collection; import java.util.Iterator; +import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; class CollectionToArrayGenericConverter implements GenericConverter { @@ -44,6 +45,9 @@ class CollectionToArrayGenericConverter implements GenericConverter { } } else { GenericConverter converter = conversionService.getConverter(sourceElementType, targetElementType); + if (converter == null) { + throw new ConverterNotFoundException(sourceType, targetType); + } for (Iterator it = sourceCollection.iterator(); it.hasNext(); i++) { Array.set(array, i, converter.convert(it.next(), sourceElementType, targetElementType)); } 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/CollectionToCollectionGenericConverter.java index 79435e0a47e..1b995236519 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/CollectionToCollectionGenericConverter.java @@ -18,6 +18,7 @@ package org.springframework.core.convert.support; import java.util.Collection; import org.springframework.core.CollectionFactory; +import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; class CollectionToCollectionGenericConverter implements GenericConverter { @@ -46,6 +47,9 @@ class CollectionToCollectionGenericConverter implements GenericConverter { } Collection targetCollection = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size()); GenericConverter converter = conversionService.getConverter(sourceElementType, targetElementType); + if (converter == null) { + throw new ConverterNotFoundException(sourceType, targetType); + } for (Object element : sourceCollection) { targetCollection.add(converter.convert(element, sourceElementType, targetElementType)); } diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToObjectGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToObjectGenericConverter.java index 125ef28cc4e..8727597aaeb 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToObjectGenericConverter.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/CollectionToObjectGenericConverter.java @@ -17,6 +17,7 @@ package org.springframework.core.convert.support; import java.util.Collection; +import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; class CollectionToObjectGenericConverter implements GenericConverter { @@ -37,6 +38,9 @@ class CollectionToObjectGenericConverter implements GenericConverter { return sourceCollection.iterator().next(); } else { GenericConverter converter = conversionService.getConverter(sourceElementType, targetType); + if (converter == null) { + throw new ConverterNotFoundException(sourceType, targetType); + } return converter.convert(sourceCollection.iterator().next(), sourceElementType, targetType); } } 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 46a9d914a4a..aaa9d235df6 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 @@ -57,7 +57,7 @@ public class GenericConversionService implements ConversionService, ConverterReg return source; } }; - + private GenericConversionService parent; private final Map> sourceTypeConverters = new HashMap>(); @@ -162,6 +162,9 @@ public class GenericConversionService implements ConversionService, ConverterReg return null; } GenericConverter converter = getConverter(sourceType, targetType); + if (converter == null) { + throw new ConverterNotFoundException(sourceType, targetType); + } try { return converter.convert(source, sourceType, targetType); } catch (ConversionFailedException e) { @@ -173,6 +176,9 @@ public class GenericConversionService implements ConversionService, ConverterReg // subclassing hooks + /** + * Hook to initialize the "generic" converters that require the full TypeDescriptor context to perform their conversion operations. + */ protected void initGenericConverters() { addGenericConverter(Object[].class, Object[].class, new ArrayToArrayGenericConverter(this)); addGenericConverter(Object[].class, Collection.class, new ArrayToCollectionGenericConverter(this)); @@ -185,14 +191,39 @@ public class GenericConversionService implements ConversionService, ConverterReg addGenericConverter(Object.class, Collection.class, new ObjectToCollectionGenericConverter(this)); } + /** + * Registers a GenericConverter. + * @param sourceType the source type to convert from + * @param targetType the target type to convert to + * @param converter the generic converter. + */ protected void addGenericConverter(Class sourceType, Class targetType, GenericConverter converter) { getSourceMap(sourceType).put(targetType, converter); } + /** + * Hook method to convert a null value. + * Default implementation simply returns null. + * Subclasses may override to return a custom null objects for specific target types. + * @param sourceType the sourceType + * @param targetType the tagetType + * @return the null object + */ protected Object convertNull(TypeDescriptor sourceType, TypeDescriptor targetType) { return null; } + /** + * Hook method to lookup the converter for a given sourceType/targetType pair. + * First queries this ConversionService's converter map. + * If no suitable Converter is found, and a {@link #setParent(GenericConversionService) parent} is set, then queries the parent. + * If still no suitable Converter is found, returns a NO_OP Converter if the sourceType and targetType are assignable. + * Returns null if this ConversionService simply cannot convert between sourceType and targetType. + * Subclasses may override. + * @param sourceType the source type to convert from + * @param targetType the target type to convert to + * @return the generic converter that will perform the conversion, or null if no suitable converter was found + */ protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { GenericConverter converter = findConverterByClassPair(sourceType.getObjectType(), targetType.getObjectType()); if (converter != null) { @@ -204,7 +235,7 @@ public class GenericConversionService implements ConversionService, ConverterReg if (sourceType.isAssignableTo(targetType)) { return NO_OP_CONVERTER; } else { - throw new ConverterNotFoundException(sourceType, targetType); + return null; } } } 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/MapToMapGenericConverter.java index 7679e2a9a43..eed7ab9bf07 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/MapToMapGenericConverter.java @@ -3,6 +3,7 @@ package org.springframework.core.convert.support; import java.util.Map; import org.springframework.core.CollectionFactory; +import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; class MapToMapGenericConverter implements GenericConverter { @@ -95,11 +96,17 @@ class MapToMapGenericConverter implements GenericConverter { GenericConversionService conversionService) { if (sourceKeyType != TypeDescriptor.NULL && targetKeyType != TypeDescriptor.NULL && !keysCompatible) { this.keyConverter = conversionService.getConverter(sourceKeyType, targetKeyType); + if (this.keyConverter == null) { + throw new ConverterNotFoundException(sourceKeyType, targetKeyType); + } this.sourceKeyType = sourceKeyType; this.targetKeyType = targetKeyType; } if (sourceValueType != TypeDescriptor.NULL && targetValueType != TypeDescriptor.NULL && !valuesCompatible) { this.valueConverter = conversionService.getConverter(sourceValueType, targetValueType); + if (this.valueConverter == null) { + throw new ConverterNotFoundException(sourceValueType, targetValueType); + } this.targetKeyType = targetKeyType; this.targetValueType = targetValueType; } diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToArrayGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToArrayGenericConverter.java index 51bab7c0d5f..da820d80bc2 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToArrayGenericConverter.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToArrayGenericConverter.java @@ -17,6 +17,7 @@ package org.springframework.core.convert.support; import java.lang.reflect.Array; +import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; class ObjectToArrayGenericConverter implements GenericConverter { @@ -34,6 +35,9 @@ class ObjectToArrayGenericConverter implements GenericConverter { Array.set(target, 0, source); } else { GenericConverter converter = conversionService.getConverter(sourceType, targetElementType); + if (converter == null) { + throw new ConverterNotFoundException(sourceType, targetType); + } Array.set(target, 0, converter.convert(source, sourceType, targetElementType)); } return target; diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionGenericConverter.java index c06569cde5c..7d8a8cedc21 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionGenericConverter.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionGenericConverter.java @@ -18,6 +18,7 @@ package org.springframework.core.convert.support; import java.util.Collection; import org.springframework.core.CollectionFactory; +import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; class ObjectToCollectionGenericConverter implements GenericConverter { @@ -27,7 +28,7 @@ class ObjectToCollectionGenericConverter implements GenericConverter { public ObjectToCollectionGenericConverter(GenericConversionService conversionService) { this.conversionService = conversionService; } - + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { Collection target = CollectionFactory.createCollection(targetType.getType(), 1); TypeDescriptor targetElementType = targetType.getElementTypeDescriptor(); @@ -35,6 +36,9 @@ class ObjectToCollectionGenericConverter implements GenericConverter { target.add(source); } else { GenericConverter converter = conversionService.getConverter(sourceType, targetElementType); + if (converter == null) { + throw new ConverterNotFoundException(sourceType, targetType); + } target.add(converter.convert(source, sourceType, targetElementType)); } return target; 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 b88585e0780..bd8b1bf24e7 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 @@ -19,6 +19,8 @@ package org.springframework.core.convert.support; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.fail; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.util.AbstractList; import java.util.ArrayList; @@ -265,6 +267,27 @@ public class GenericConversionServiceTests { assertEquals(new Integer(3), result[2]); } + @Test + public void genericConverterDelegatingBackToConversionServiceConverterNotFound() { + try { + converter.convert("1", Integer[].class); + } catch (ConversionFailedException e) { + assertTrue(e.getCause() instanceof ConverterNotFoundException); + } + } + + @Test + public void parent() { + GenericConversionService parent = new GenericConversionService(); + converter.setParent(parent); + assertFalse(converter.canConvert(String.class, Integer.class)); + try { + converter.convert("3", Integer.class); + } catch (ConverterNotFoundException e) { + + } + } + public static enum FooEnum { BAR, BAZ }