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 c3c1372c645..4c8c686845a 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 @@ -38,42 +38,41 @@ class CollectionToCollectionGenericConverter implements GenericConverter { public Object convert(Object source, TypeDescriptor targetType) { Collection sourceCollection = (Collection) source; - Object firstNotNullElement = getFirstNotNullElement(sourceCollection); - if (firstNotNullElement == null) { + Class targetElementType = targetType.getElementType(); + if (targetElementType == null) { return compatibleCollectionWithoutElementConversion(sourceCollection, targetType); } - Class targetElementType = targetType.getElementType(); - if (targetElementType == null || targetElementType.isAssignableFrom(firstNotNullElement.getClass())) { + Class sourceElementType = getElementType(sourceCollection); + if (sourceElementType == null || targetElementType.isAssignableFrom(sourceElementType)) { return compatibleCollectionWithoutElementConversion(sourceCollection, targetType); } Collection targetCollection = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size()); - GenericConverter elementConverter = conversionService.getConverter(firstNotNullElement.getClass(), - TypeDescriptor.valueOf(targetElementType)); + TypeDescriptor targetElementTypeDescriptor = TypeDescriptor.valueOf(targetElementType); + GenericConverter elementConverter = conversionService.getConverter(sourceElementType, + targetElementTypeDescriptor); for (Object element : sourceCollection) { - targetCollection.add(elementConverter.convert(element, TypeDescriptor.valueOf(targetElementType))); + targetCollection.add(elementConverter.convert(element, targetElementTypeDescriptor)); } return targetCollection; } + private Class getElementType(Collection collection) { + for (Object element : collection) { + if (element != null) { + return element.getClass(); + } + } + return null; + } + private Collection compatibleCollectionWithoutElementConversion(Collection source, TypeDescriptor targetType) { if (targetType.getType().isAssignableFrom(source.getClass())) { return source; } else { Collection target = CollectionFactory.createCollection(targetType.getType(), source.size()); - for (Object element : source) { - target.addAll(source); - } + target.addAll(source); return target; } } - private Object getFirstNotNullElement(Collection collection) { - for (Object element : collection) { - if (element != null) { - return element; - } - } - 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 b4f8125044b..914b69fc8b4 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 @@ -58,6 +58,7 @@ public class GenericConversionService implements ConversionService, ConverterReg public GenericConversionService() { addGenericConverter(Collection.class, Collection.class, new CollectionToCollectionGenericConverter(this)); + addGenericConverter(Map.class, Map.class, new MapToMapGenericConverter(this)); } /** 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 new file mode 100644 index 00000000000..4eb57b8979d --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/MapToMapGenericConverter.java @@ -0,0 +1,120 @@ +package org.springframework.core.convert.support; + +import java.util.Map; + +import org.springframework.core.CollectionFactory; +import org.springframework.core.convert.TypeDescriptor; + +class MapToMapGenericConverter implements GenericConverter { + + private GenericConversionService conversionService; + + public MapToMapGenericConverter(GenericConversionService conversionService) { + this.conversionService = conversionService; + } + + public Object convert(Object source, TypeDescriptor targetType) { + Map sourceMap = (Map) source; + Class targetKeyType = targetType.getMapKeyType(); + Class targetValueType = targetType.getMapValueType(); + if (targetKeyType == null && targetValueType == null) { + return compatibleMapWithoutEntryConversion(sourceMap, targetType); + } + Class[] sourceEntryTypes = getMapEntryTypes(sourceMap); + Class sourceKeyType = sourceEntryTypes[0]; + Class sourceValueType = sourceEntryTypes[1]; + if (sourceKeyType == null && sourceValueType == null) { + return compatibleMapWithoutEntryConversion(sourceMap, targetType); + } + boolean keysCompatible = false; + if (targetKeyType != null && sourceKeyType != null && targetKeyType.isAssignableFrom(sourceKeyType)) { + keysCompatible = true; + } + boolean valuesCompatible = false; + if (targetValueType != null && sourceValueType != null && targetValueType.isAssignableFrom(sourceValueType)) { + valuesCompatible = true; + } + if (keysCompatible && valuesCompatible) { + return compatibleMapWithoutEntryConversion(sourceMap, targetType); + } + Map targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size()); + MapEntryConverter converter = new MapEntryConverter(sourceKeyType, sourceValueType, targetKeyType, targetValueType, keysCompatible, valuesCompatible, conversionService); + for (Object entry : sourceMap.entrySet()) { + Map.Entry sourceMapEntry = (Map.Entry) entry; + targetMap.put(converter.convertKey(sourceMapEntry.getKey()), converter.convertValue(sourceMapEntry.getValue())); + } + return targetMap; + } + + private Class[] getMapEntryTypes(Map sourceMap) { + Class keyType = null; + Class valueType = null; + for (Object entry : sourceMap.entrySet()) { + Map.Entry mapEntry = (Map.Entry) entry; + Object key = mapEntry.getKey(); + if (keyType == null && key != null) { + keyType = key.getClass(); + } + Object value = mapEntry.getValue(); + if (valueType == null && value != null) { + valueType = value.getClass(); + } + if (mapEntry.getKey() != null && mapEntry.getValue() != null) { + break; + } + } + return new Class[] { keyType, valueType }; + } + + private Map compatibleMapWithoutEntryConversion(Map source, TypeDescriptor targetType) { + if (targetType.getType().isAssignableFrom(source.getClass())) { + return source; + } else { + Map target = CollectionFactory.createMap(targetType.getType(), source.size()); + target.putAll(source); + return target; + } + } + + private static class MapEntryConverter { + + private GenericConverter keyConverter; + + private GenericConverter valueConverter; + + private TypeDescriptor targetKeyTypeDescriptor; + + private TypeDescriptor targetValueTypeDescriptor; + + public MapEntryConverter(Class sourceKeyType, Class sourceValueType, Class targetKeyType, + Class 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 (sourceValueType != null && targetValueType != null && !valuesCompatible) { + this.targetValueTypeDescriptor = TypeDescriptor.valueOf(targetValueType); + this.valueConverter = conversionService.getConverter(sourceValueType, targetValueTypeDescriptor); + } + } + + public Object convertKey(Object sourceKey) { + if (sourceKey != null && this.keyConverter != null) { + return this.keyConverter.convert(sourceKey, targetKeyTypeDescriptor); + } else { + return sourceKey; + } + } + + public Object convertValue(Object sourceValue) { + if (sourceValue != null && this.valueConverter != null) { + return this.valueConverter.convert(sourceValue, targetValueTypeDescriptor); + } else { + return sourceValue; + } + } + + } + +} 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 416c5eb85d9..6c840a2c078 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 @@ -230,7 +230,6 @@ public class GenericConversionServiceTests { public Map genericMap = new HashMap(); @Test - @Ignore public void convertMapToMap() throws Exception { Map foo = new HashMap(); foo.put("1", "BAR");