From fef4cd0ed6ff98c53874bb356e74ce51b1a50b91 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 28 Nov 2014 20:30:46 +0100 Subject: [PATCH] Default conversion support for EnumSet / EnumMap Issue: SPR-12483 --- .../core/CollectionFactory.java | 148 ++++++++++----- .../support/ArrayToCollectionConverter.java | 22 ++- .../CollectionToCollectionConverter.java | 4 +- .../convert/support/MapToMapConverter.java | 17 +- .../support/ObjectToCollectionConverter.java | 14 +- .../support/StringToCollectionConverter.java | 11 +- .../CollectionToCollectionConverterTests.java | 54 ++++-- .../GenericConversionServiceTests.java | 179 +++++++++--------- .../support/MapToMapConverterTests.java | 63 ++++-- 9 files changed, 315 insertions(+), 197 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/CollectionFactory.java b/spring-core/src/main/java/org/springframework/core/CollectionFactory.java index 998bd813e49..39994a3d76d 100644 --- a/spring-core/src/main/java/org/springframework/core/CollectionFactory.java +++ b/spring-core/src/main/java/org/springframework/core/CollectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -18,6 +18,8 @@ package org.springframework.core; import java.util.ArrayList; import java.util.Collection; +import java.util.EnumMap; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -33,6 +35,7 @@ import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; +import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -72,9 +75,11 @@ public abstract class CollectionFactory { approximableCollectionTypes.add(HashSet.class); approximableCollectionTypes.add(LinkedHashSet.class); approximableCollectionTypes.add(TreeSet.class); + approximableCollectionTypes.add(EnumSet.class); approximableMapTypes.add(HashMap.class); approximableMapTypes.add(LinkedHashMap.class); approximableMapTypes.add(TreeMap.class); + approximableMapTypes.add(EnumMap.class); } @@ -91,68 +96,87 @@ public abstract class CollectionFactory { /** * Create the most approximate collection for the given collection. - *

Creates an ArrayList, TreeSet or linked Set for a List, SortedSet - * or Set, respectively. * @param collection the original Collection object - * @param initialCapacity the initial capacity + * @param capacity the initial capacity * @return the new Collection instance - * @see java.util.ArrayList - * @see java.util.TreeSet * @see java.util.LinkedHashSet + * @see java.util.TreeSet + * @see java.util.EnumSet + * @see java.util.ArrayList + * @see java.util.LinkedList */ @SuppressWarnings("unchecked") - public static Collection createApproximateCollection(Object collection, int initialCapacity) { + public static Collection createApproximateCollection(Object collection, int capacity) { if (collection instanceof LinkedList) { return new LinkedList(); } else if (collection instanceof List) { - return new ArrayList(initialCapacity); + return new ArrayList(capacity); + } + else if (collection instanceof EnumSet) { + return EnumSet.copyOf((Collection) collection); } else if (collection instanceof SortedSet) { return new TreeSet(((SortedSet) collection).comparator()); } else { - return new LinkedHashSet(initialCapacity); + return new LinkedHashSet(capacity); } } /** * Create the most appropriate collection for the given collection type. - *

Creates an ArrayList, TreeSet or linked Set for a List, SortedSet - * or Set, respectively. - * @param collectionType the desired type of the target Collection - * @param initialCapacity the initial capacity + *

Delegates to {@link #createCollection(Class, Class, int)} with a + * {@code null} element type. + * @param collectionClass the desired type of the target Collection + * @param capacity the initial capacity + * @return the new Collection instance + */ + public static Collection createCollection(Class collectionClass, int capacity) { + return createCollection(collectionClass, null, capacity); + } + + /** + * Create the most appropriate collection for the given collection type. + * @param collectionClass the desired type of the target Collection + * @param elementType the collection's element type, or {@code null} if not known + * @param capacity the initial capacity * @return the new Collection instance - * @see java.util.ArrayList - * @see java.util.TreeSet * @see java.util.LinkedHashSet + * @see java.util.TreeSet + * @see java.util.EnumSet + * @see java.util.ArrayList */ @SuppressWarnings("unchecked") - public static Collection createCollection(Class collectionType, int initialCapacity) { - if (collectionType.isInterface()) { - if (List.class.equals(collectionType)) { - return new ArrayList(initialCapacity); + public static Collection createCollection(Class collectionClass, Class elementType, int capacity) { + if (collectionClass.isInterface()) { + if (Set.class.equals(collectionClass) || Collection.class.equals(collectionClass)) { + return new LinkedHashSet(capacity); } - else if (SortedSet.class.equals(collectionType) || NavigableSet.class.equals(collectionType)) { - return new TreeSet(); + else if (List.class.equals(collectionClass)) { + return new ArrayList(capacity); } - else if (Set.class.equals(collectionType) || Collection.class.equals(collectionType)) { - return new LinkedHashSet(initialCapacity); + else if (SortedSet.class.equals(collectionClass) || NavigableSet.class.equals(collectionClass)) { + return new TreeSet(); } else { - throw new IllegalArgumentException("Unsupported Collection interface: " + collectionType.getName()); + throw new IllegalArgumentException("Unsupported Collection interface: " + collectionClass.getName()); } } + else if (EnumSet.class.equals(collectionClass)) { + Assert.notNull(elementType, "Cannot create EnumSet for unknown element type"); + return EnumSet.noneOf((Class) elementType); + } else { - if (!Collection.class.isAssignableFrom(collectionType)) { - throw new IllegalArgumentException("Unsupported Collection type: " + collectionType.getName()); + if (!Collection.class.isAssignableFrom(collectionClass)) { + throw new IllegalArgumentException("Unsupported Collection type: " + collectionClass.getName()); } try { - return (Collection) collectionType.newInstance(); + return (Collection) collectionClass.newInstance(); } catch (Exception ex) { - throw new IllegalArgumentException("Could not instantiate Collection type: " + - collectionType.getName(), ex); + throw new IllegalArgumentException( + "Could not instantiate Collection type: " + collectionClass.getName(), ex); } } } @@ -170,58 +194,78 @@ public abstract class CollectionFactory { /** * Create the most approximate map for the given map. - *

Creates a TreeMap or linked Map for a SortedMap or Map, respectively. * @param map the original Map object - * @param initialCapacity the initial capacity + * @param capacity the initial capacity * @return the new Map instance * @see java.util.TreeMap * @see java.util.LinkedHashMap */ - @SuppressWarnings("unchecked") - public static Map createApproximateMap(Object map, int initialCapacity) { - if (map instanceof SortedMap) { + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Map createApproximateMap(Object map, int capacity) { + if (map instanceof EnumMap) { + return new EnumMap((Map) map); + } + else if (map instanceof SortedMap) { return new TreeMap(((SortedMap) map).comparator()); } else { - return new LinkedHashMap(initialCapacity); + return new LinkedHashMap(capacity); } } /** * Create the most approximate map for the given map. - *

Creates a TreeMap or linked Map for a SortedMap or Map, respectively. - * @param mapType the desired type of the target Map - * @param initialCapacity the initial capacity + *

Delegates to {@link #createMap(Class, Class, int)} with a + * {@code null} key type. + * @param mapClass the desired type of the target Map + * @param capacity the initial capacity + * @return the new Map instance + */ + public static Map createMap(Class mapClass, int capacity) { + return createMap(mapClass, null, capacity); + } + + /** + * Create the most approximate map for the given map. + * @param mapClass the desired type of the target Map + * @param keyType the map's key type, or {@code null} if not known + * @param capacity the initial capacity * @return the new Map instance - * @see java.util.TreeMap * @see java.util.LinkedHashMap + * @see java.util.TreeMap + * @see java.util.EnumMap + * @see org.springframework.util.LinkedMultiValueMap */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Map createMap(Class mapType, int initialCapacity) { - if (mapType.isInterface()) { - if (Map.class.equals(mapType)) { - return new LinkedHashMap(initialCapacity); + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Map createMap(Class mapClass, Class keyType, int capacity) { + if (mapClass.isInterface()) { + if (Map.class.equals(mapClass)) { + return new LinkedHashMap(capacity); } - else if (SortedMap.class.equals(mapType) || NavigableMap.class.equals(mapType)) { + else if (SortedMap.class.equals(mapClass) || NavigableMap.class.equals(mapClass)) { return new TreeMap(); } - else if (MultiValueMap.class.equals(mapType)) { + else if (MultiValueMap.class.equals(mapClass)) { return new LinkedMultiValueMap(); } else { - throw new IllegalArgumentException("Unsupported Map interface: " + mapType.getName()); + throw new IllegalArgumentException("Unsupported Map interface: " + mapClass.getName()); } } + else if (EnumMap.class.equals(mapClass)) { + Assert.notNull(keyType, "Cannot create EnumMap for unknown key type"); + return new EnumMap(keyType); + } else { - if (!Map.class.isAssignableFrom(mapType)) { - throw new IllegalArgumentException("Unsupported Map type: " + mapType.getName()); + if (!Map.class.isAssignableFrom(mapClass)) { + throw new IllegalArgumentException("Unsupported Map type: " + mapClass.getName()); } try { - return (Map) mapType.newInstance(); + return (Map) mapClass.newInstance(); } catch (Exception ex) { - throw new IllegalArgumentException("Could not instantiate Map type: " + - mapType.getName(), ex); + throw new IllegalArgumentException( + "Could not instantiate Map type: " + mapClass.getName(), ex); } } } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionConverter.java index 30f3de5593d..22373712694 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ArrayToCollectionConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 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. @@ -27,23 +27,27 @@ import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.ConditionalGenericConverter; /** - * Converts an Array to a Collection. + * Converts an array to a Collection. * - *

First, creates a new Collection of the requested targetType. + *

First, creates a new Collection of the requested target type. * Then adds each array element to the target collection. - * Will perform an element conversion from the source component type to the collection's parameterized type if necessary. + * Will perform an element conversion from the source component type + * to the collection's parameterized type if necessary. * * @author Keith Donald + * @author Juergen Hoeller * @since 3.0 */ final class ArrayToCollectionConverter implements ConditionalGenericConverter { private final ConversionService conversionService; + public ArrayToCollectionConverter(ConversionService conversionService) { this.conversionService = conversionService; } + @Override public Set getConvertibleTypes() { return Collections.singleton(new ConvertiblePair(Object[].class, Collection.class)); @@ -60,9 +64,13 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter { if (source == null) { return null; } + int length = Array.getLength(source); - Collection target = CollectionFactory.createCollection(targetType.getType(), length); - if (targetType.getElementTypeDescriptor() == null) { + TypeDescriptor elementDesc = targetType.getElementTypeDescriptor(); + Collection target = CollectionFactory.createCollection(targetType.getType(), + (elementDesc != null ? elementDesc.getType() : null), length); + + if (elementDesc == null) { for (int i = 0; i < length; i++) { Object sourceElement = Array.get(source, i); target.add(sourceElement); @@ -72,7 +80,7 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter { for (int i = 0; i < length; i++) { Object sourceElement = Array.get(source, i); Object targetElement = this.conversionService.convert(sourceElement, - sourceType.elementTypeDescriptor(sourceElement), targetType.getElementTypeDescriptor()); + sourceType.elementTypeDescriptor(sourceElement), elementDesc); target.add(targetElement); } } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionConverter.java index a8071eae58a..f5addb2ae46 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/CollectionToCollectionConverter.java @@ -76,7 +76,9 @@ final class CollectionToCollectionConverter implements ConditionalGenericConvert } // At this point, we need a collection copy in any case, even if just for finding out about element copies... - Collection target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size()); + Collection target = CollectionFactory.createCollection(targetType.getType(), + (elementDesc != null ? elementDesc.getType() : null), sourceCollection.size()); + if (elementDesc == null) { target.addAll(sourceCollection); } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/MapToMapConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/MapToMapConverter.java index 0a300b2588d..b5225c7e810 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/MapToMapConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/MapToMapConverter.java @@ -36,6 +36,7 @@ import org.springframework.core.convert.converter.ConditionalGenericConverter; * map's parameterized types K,V if necessary. * * @author Keith Donald + * @author Juergen Hoeller * @since 3.0 */ final class MapToMapConverter implements ConditionalGenericConverter { @@ -64,17 +65,22 @@ final class MapToMapConverter implements ConditionalGenericConverter { if (source == null) { return null; } - boolean copyRequired = !targetType.getType().isInstance(source); Map sourceMap = (Map) source; + + // Shortcut if possible... + boolean copyRequired = !targetType.getType().isInstance(source); if (!copyRequired && sourceMap.isEmpty()) { return sourceMap; } + TypeDescriptor keyDesc = targetType.getMapKeyTypeDescriptor(); + TypeDescriptor valueDesc = targetType.getMapValueTypeDescriptor(); + List targetEntries = new ArrayList(sourceMap.size()); for (Map.Entry entry : sourceMap.entrySet()) { Object sourceKey = entry.getKey(); Object sourceValue = entry.getValue(); - Object targetKey = convertKey(sourceKey, sourceType, targetType.getMapKeyTypeDescriptor()); - Object targetValue = convertValue(sourceValue, sourceType, targetType.getMapValueTypeDescriptor()); + Object targetKey = convertKey(sourceKey, sourceType, keyDesc); + Object targetValue = convertValue(sourceValue, sourceType, valueDesc); targetEntries.add(new MapEntry(targetKey, targetValue)); if (sourceKey != targetKey || sourceValue != targetValue) { copyRequired = true; @@ -83,7 +89,10 @@ final class MapToMapConverter implements ConditionalGenericConverter { if (!copyRequired) { return sourceMap; } - Map targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size()); + + Map targetMap = CollectionFactory.createMap(targetType.getType(), + (keyDesc != null ? keyDesc.getType() : null), sourceMap.size()); + for (MapEntry entry : targetEntries) { entry.addToMap(targetMap); } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionConverter.java index a2e98b321ae..5c073463218 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToCollectionConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 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. @@ -37,10 +37,12 @@ final class ObjectToCollectionConverter implements ConditionalGenericConverter { private final ConversionService conversionService; + public ObjectToCollectionConverter(ConversionService conversionService) { this.conversionService = conversionService; } + @Override public Set getConvertibleTypes() { return Collections.singleton(new ConvertiblePair(Object.class, Collection.class)); @@ -56,12 +58,16 @@ final class ObjectToCollectionConverter implements ConditionalGenericConverter { if (source == null) { return null; } - Collection target = CollectionFactory.createCollection(targetType.getType(), 1); - if (targetType.getElementTypeDescriptor() == null || targetType.getElementTypeDescriptor().isCollection()) { + + TypeDescriptor elementDesc = targetType.getElementTypeDescriptor(); + Collection target = CollectionFactory.createCollection(targetType.getType(), + (elementDesc != null ? elementDesc.getType() : null), 1); + + if (elementDesc == null || elementDesc.isCollection()) { target.add(source); } else { - Object singleElement = this.conversionService.convert(source, sourceType, targetType.getElementTypeDescriptor()); + Object singleElement = this.conversionService.convert(source, sourceType, elementDesc); target.add(singleElement); } return target; diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/StringToCollectionConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/StringToCollectionConverter.java index 9c23d2c7690..8311e463794 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/StringToCollectionConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/StringToCollectionConverter.java @@ -32,6 +32,7 @@ import org.springframework.util.StringUtils; * {@code String.class} can be converted to it. * * @author Keith Donald + * @author Juergen Hoeller * @since 3.0 */ final class StringToCollectionConverter implements ConditionalGenericConverter { @@ -61,16 +62,20 @@ final class StringToCollectionConverter implements ConditionalGenericConverter { return null; } String string = (String) source; + String[] fields = StringUtils.commaDelimitedListToStringArray(string); - Collection target = CollectionFactory.createCollection(targetType.getType(), fields.length); - if (targetType.getElementTypeDescriptor() == null) { + TypeDescriptor elementDesc = targetType.getElementTypeDescriptor(); + Collection target = CollectionFactory.createCollection(targetType.getType(), + (elementDesc != null ? elementDesc.getType() : null), fields.length); + + if (elementDesc == null) { for (String field : fields) { target.add(field.trim()); } } else { for (String field : fields) { - Object targetElement = this.conversionService.convert(field.trim(), sourceType, targetType.getElementTypeDescriptor()); + Object targetElement = this.conversionService.convert(field.trim(), sourceType, elementDesc); target.add(targetElement); } } diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java index 00bf054a66d..1eb22e74d47 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.LinkedList; import java.util.List; import java.util.Vector; @@ -199,7 +200,7 @@ public class CollectionToCollectionConverterTests { public void listToCollectionNoCopyRequired() throws NoSuchFieldException { List input = new ArrayList(Arrays.asList("foo", "bar")); assertSame(input, conversionService.convert(input, TypeDescriptor.forObject(input), - new TypeDescriptor(getClass().getField("wildCardCollection")))); + new TypeDescriptor(getClass().getField("wildcardCollection")))); } @Test @@ -232,7 +233,7 @@ public class CollectionToCollectionConverterTests { assertSame(resources, conversionService.convert(resources, sourceType, new TypeDescriptor(getClass().getField("resources")))); } - @Test(expected=ConverterNotFoundException.class) + @Test(expected = ConverterNotFoundException.class) public void elementTypesNotConvertible() throws Exception { List resources = new ArrayList(); resources.add(null); @@ -241,7 +242,7 @@ public class CollectionToCollectionConverterTests { assertEquals(resources, conversionService.convert(resources, sourceType, new TypeDescriptor(getClass().getField("resources")))); } - @Test(expected=ConversionFailedException.class) + @Test(expected = ConversionFailedException.class) public void nothingInCommon() throws Exception { List resources = new ArrayList(); resources.add(new ClassPathResource("test")); @@ -250,22 +251,15 @@ public class CollectionToCollectionConverterTests { assertEquals(resources, conversionService.convert(resources, sourceType, new TypeDescriptor(getClass().getField("resources")))); } - - public ArrayList scalarListTarget; - - public List emptyListTarget; - - public LinkedList emptyListDifferentTarget; - - public List>> objectToCollection; - - public List strings; - - public List list = Collections.emptyList(); - - public Collection wildCardCollection = Collections.emptyList(); - - public List resources; + @Test + public void testStringToEnumSet() throws Exception { + conversionService.addConverterFactory(new StringToEnumConverterFactory()); + List list = new ArrayList(); + list.add("A"); + list.add("C"); + assertEquals(EnumSet.of(MyEnum.A, MyEnum.C), + conversionService.convert(list, TypeDescriptor.forObject(list), new TypeDescriptor(getClass().getField("enumSet")))); + } public static abstract class BaseResource implements Resource { @@ -335,4 +329,26 @@ public class CollectionToCollectionConverterTests { public static class TestResource extends BaseResource { } + + public static enum MyEnum {A, B, C} + + + public ArrayList scalarListTarget; + + public List emptyListTarget; + + public LinkedList emptyListDifferentTarget; + + public List>> objectToCollection; + + public List strings; + + public List list = Collections.emptyList(); + + public Collection wildcardCollection = Collections.emptyList(); + + public List resources; + + public EnumSet enumSet; + } diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java index fca9caf6fbd..dc87b5f0bcf 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -445,8 +446,6 @@ public class GenericConversionServiceTests { System.out.println(watch.prettyPrint()); } - public static List list; - @Test public void testPerformance3() throws Exception { Assume.group(TestGroup.PERFORMANCE); @@ -473,8 +472,6 @@ public class GenericConversionServiceTests { System.out.println(watch.prettyPrint()); } - public static Map map; - @Test public void emptyListToArray() { conversionService.addConverter(new CollectionToArrayConverter(conversionService)); @@ -483,7 +480,7 @@ public class GenericConversionServiceTests { TypeDescriptor sourceType = TypeDescriptor.forObject(list); TypeDescriptor targetType = TypeDescriptor.valueOf(String[].class); assertTrue(conversionService.canConvert(sourceType, targetType)); - assertEquals(0, ((String[])conversionService.convert(list, sourceType, targetType)).length); + assertEquals(0, ((String[]) conversionService.convert(list, sourceType, targetType)).length); } @Test @@ -497,82 +494,6 @@ public class GenericConversionServiceTests { assertNull(conversionService.convert(list, sourceType, targetType)); } - private interface MyBaseInterface { - - } - - - private interface MyInterface extends MyBaseInterface { - - } - - - private static class MyInterfaceImplementer implements MyInterface { - - } - - - private static class MyBaseInterfaceConverter implements Converter { - - @Override - public String convert(MyBaseInterface source) { - return "RESULT"; - } - } - - - private static class MyStringArrayToResourceArrayConverter implements Converter { - - @Override - public Resource[] convert(String[] source) { - Resource[] result = new Resource[source.length]; - for (int i = 0; i < source.length; i++) { - result[i] = new DescriptiveResource(source[i].substring(1)); - } - return result; - } - } - - - private static class MyStringArrayToIntegerArrayConverter implements Converter { - - @Override - public Integer[] convert(String[] source) { - Integer[] result = new Integer[source.length]; - for (int i = 0; i < source.length; i++) { - result[i] = Integer.parseInt(source[i].substring(1)); - } - return result; - } - } - - - private static class MyStringToIntegerArrayConverter implements Converter { - - @Override - public Integer[] convert(String source) { - String[] srcArray = StringUtils.commaDelimitedListToStringArray(source); - Integer[] result = new Integer[srcArray.length]; - for (int i = 0; i < srcArray.length; i++) { - result[i] = Integer.parseInt(srcArray[i].substring(1)); - } - return result; - } - } - - - public static class WithCopyConstructor { - - public WithCopyConstructor() { - } - - public WithCopyConstructor(WithCopyConstructor value) { - } - } - - - public static Map wildcardMap; - @Test public void stringToArrayCanConvert() { conversionService.addConverter(new StringToArrayConverter(conversionService)); @@ -585,14 +506,12 @@ public class GenericConversionServiceTests { public void stringToCollectionCanConvert() throws Exception { conversionService.addConverter(new StringToCollectionConverter(conversionService)); assertTrue(conversionService.canConvert(String.class, Collection.class)); - TypeDescriptor targetType = new TypeDescriptor(getClass().getField("stringToCollection")); + TypeDescriptor targetType = new TypeDescriptor(getClass().getField("integerCollection")); assertFalse(conversionService.canConvert(TypeDescriptor.valueOf(String.class), targetType)); conversionService.addConverterFactory(new StringToNumberConverterFactory()); assertTrue(conversionService.canConvert(TypeDescriptor.valueOf(String.class), targetType)); } - public Collection stringToCollection; - @Test public void testConvertiblePairsInSet() { Set set = new HashSet(); @@ -781,6 +700,13 @@ public class GenericConversionServiceTests { assertEquals(MyEnum.A, conversionService.convert("base1", MyEnum.class)); } + @Test + public void testStringToEnumSet() throws Exception { + DefaultConversionService.addDefaultConverters(conversionService); + assertEquals(EnumSet.of(MyEnum.A), + conversionService.convert("A", TypeDescriptor.valueOf(String.class), new TypeDescriptor(getClass().getField("enumSet")))); + } + @Test public void convertNullAnnotatedStringToString() throws Exception { DefaultConversionService.addDefaultConverters(conversionService); @@ -870,15 +796,80 @@ public class GenericConversionServiceTests { } - @ExampleAnnotation - public String annotatedString; - - @Retention(RetentionPolicy.RUNTIME) public static @interface ExampleAnnotation { } + private interface MyBaseInterface { + } + + + private interface MyInterface extends MyBaseInterface { + } + + + private static class MyInterfaceImplementer implements MyInterface { + } + + + private static class MyBaseInterfaceConverter implements Converter { + + @Override + public String convert(MyBaseInterface source) { + return "RESULT"; + } + } + + + private static class MyStringArrayToResourceArrayConverter implements Converter { + + @Override + public Resource[] convert(String[] source) { + Resource[] result = new Resource[source.length]; + for (int i = 0; i < source.length; i++) { + result[i] = new DescriptiveResource(source[i].substring(1)); + } + return result; + } + } + + + private static class MyStringArrayToIntegerArrayConverter implements Converter { + + @Override + public Integer[] convert(String[] source) { + Integer[] result = new Integer[source.length]; + for (int i = 0; i < source.length; i++) { + result[i] = Integer.parseInt(source[i].substring(1)); + } + return result; + } + } + + + private static class MyStringToIntegerArrayConverter implements Converter { + + @Override + public Integer[] convert(String source) { + String[] srcArray = StringUtils.commaDelimitedListToStringArray(source); + Integer[] result = new Integer[srcArray.length]; + for (int i = 0; i < srcArray.length; i++) { + result[i] = Integer.parseInt(srcArray[i].substring(1)); + } + return result; + } + } + + + public static class WithCopyConstructor { + + public WithCopyConstructor() { + } + + public WithCopyConstructor(WithCopyConstructor value) { + } + } private static class MyConditionalConverter implements Converter, ConditionalConverter { private int matchAttempts = 0; @@ -1062,6 +1053,7 @@ public class GenericConversionServiceTests { } private static class StringToMyEnumBaseInterfaceConverter & MyEnumBaseInterface> implements Converter { + private final Class enumType; public StringToMyEnumBaseInterfaceConverter(Class enumType) { @@ -1098,6 +1090,17 @@ public class GenericConversionServiceTests { } + @ExampleAnnotation + public String annotatedString; + + public List list; + + public Map map; + + public Map wildcardMap; + + public EnumSet enumSet; + public Collection rawCollection; public Collection genericCollection; diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/MapToMapConverterTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/MapToMapConverterTests.java index ae6db0c2897..86c1cfbfefa 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/MapToMapConverterTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/MapToMapConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 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. @@ -18,6 +18,7 @@ package org.springframework.core.convert.support; import java.util.Arrays; import java.util.Collections; +import java.util.EnumMap; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -39,11 +40,13 @@ public class MapToMapConverterTests { private GenericConversionService conversionService = new GenericConversionService(); + @Before public void setUp() { conversionService.addConverter(new MapToMapConverter(conversionService)); } + @Test public void scalarMap() throws Exception { Map map = new HashMap(); @@ -66,8 +69,6 @@ public class MapToMapConverterTests { assertEquals((Integer) 37, result.get(2)); } - public Map scalarMapTarget; - @Test public void scalarMapNotGenericTarget() throws Exception { Map map = new HashMap(); @@ -99,8 +100,6 @@ public class MapToMapConverterTests { assertEquals((Integer) 37, result.get(2)); } - public Map notGenericMapSource; - @Test public void collectionMap() throws Exception { Map> map = new HashMap>(); @@ -124,8 +123,6 @@ public class MapToMapConverterTests { assertEquals(Arrays.asList(37, 23), result.get(2)); } - public Map> collectionMapTarget; - @Test public void collectionMapSourceTarget() throws Exception { Map> map = new HashMap>(); @@ -137,8 +134,9 @@ public class MapToMapConverterTests { try { conversionService.convert(map, sourceType, targetType); fail("Should have failed"); - } catch (ConverterNotFoundException e) { - + } + catch (ConverterNotFoundException ex) { + // expected } conversionService.addConverter(new CollectionToCollectionConverter(conversionService)); conversionService.addConverterFactory(new StringToNumberConverterFactory()); @@ -150,8 +148,6 @@ public class MapToMapConverterTests { assertEquals(Arrays.asList(37, 23), result.get(2)); } - public Map> sourceCollectionMapTarget; - @Test public void collectionMapNotGenericTarget() throws Exception { Map> map = new HashMap>(); @@ -181,8 +177,6 @@ public class MapToMapConverterTests { assertSame(map, conversionService.convert(map, sourceType, targetType)); } - public Map emptyMapTarget; - @Test public void emptyMapNoTargetGenericInfo() throws Exception { Map map = new HashMap(); @@ -202,8 +196,6 @@ public class MapToMapConverterTests { assertEquals(LinkedHashMap.class, result.getClass()); } - public LinkedHashMap emptyMapDifferentTarget; - @Test public void noDefaultConstructorCopyNotRequired() throws Exception { // SPR-9284 @@ -220,8 +212,6 @@ public class MapToMapConverterTests { assertEquals(NoDefaultConstructorMap.class, result.getClass()); } - public MultiValueMap multiValueMapTarget; - @Test @SuppressWarnings("unchecked") public void multiValueMapToMultiValueMap() throws Exception { @@ -250,11 +240,46 @@ public class MapToMapConverterTests { assertThat(converted.get("b"), equalTo(Arrays.asList("2"))); } + @Test + public void testStringToEnumMap() throws Exception { + conversionService.addConverterFactory(new StringToEnumConverterFactory()); + Map source = new HashMap(); + source.put("A", 1); + source.put("C", 2); + EnumMap result = new EnumMap(MyEnum.class); + result.put(MyEnum.A, 1); + result.put(MyEnum.C, 2); + assertEquals(result, + conversionService.convert(source, TypeDescriptor.forObject(source), new TypeDescriptor(getClass().getField("enumMap")))); + } + + @SuppressWarnings("serial") public static class NoDefaultConstructorMap extends HashMap { - public NoDefaultConstructorMap(Map m) { - super(m); + + public NoDefaultConstructorMap(Map map) { + super(map); } } + + public static enum MyEnum {A, B, C} + + + public Map scalarMapTarget; + + public Map> collectionMapTarget; + + public Map> sourceCollectionMapTarget; + + public Map emptyMapTarget; + + public LinkedHashMap emptyMapDifferentTarget; + + public MultiValueMap multiValueMapTarget; + + public Map notGenericMapSource; + + public EnumMap enumMap; + }