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 822bfb327fb..33fe18def1f 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 @@ -19,6 +19,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; +import java.util.Map; import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.MethodParameter; @@ -135,6 +136,13 @@ public class TypeDescriptor { return Collection.class.isAssignableFrom(getType()); } + /** + * Is this type a {@link Map} type? + */ + public boolean isMap() { + return Map.class.isAssignableFrom(getType()); + } + /** * If this type is an array type or {@link Collection} type, returns the underlying element type. * Returns null if the type is neither an array or collection. diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/service/ArrayToCollection.java b/org.springframework.core/src/main/java/org/springframework/core/convert/service/ArrayToCollection.java index a8cba463588..d1fa7bb204d 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/service/ArrayToCollection.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/service/ArrayToCollection.java @@ -16,7 +16,6 @@ package org.springframework.core.convert.service; import java.lang.reflect.Array; -import java.lang.reflect.Constructor; import java.util.Collection; import org.springframework.core.convert.ConversionExecutor; @@ -47,8 +46,7 @@ class ArrayToCollection extends AbstractCollectionConverter { @SuppressWarnings("unchecked") protected Object doExecute(Object sourceArray) throws Exception { Class implClass = CollectionConversionUtils.getImpl(getTargetType().getType()); - Constructor constructor = implClass.getConstructor((Class[]) null); - Collection collection = (Collection) constructor.newInstance((Object[]) null); + Collection collection = (Collection) implClass.newInstance(); int length = Array.getLength(sourceArray); ConversionExecutor converter = getElementConverter(); for (int i = 0; i < length; i++) { diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/service/CollectionToCollection.java b/org.springframework.core/src/main/java/org/springframework/core/convert/service/CollectionToCollection.java index 954a7664981..e58b58841ea 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/service/CollectionToCollection.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/service/CollectionToCollection.java @@ -40,8 +40,7 @@ class CollectionToCollection extends AbstractCollectionConverter { Collection sourceCollection = (Collection) source; Class targetCollectionType = getTargetType().getType(); Class implClass = CollectionConversionUtils.getImpl(targetCollectionType); - Collection targetCollection = (Collection) implClass.getConstructor((Class[]) null) - .newInstance((Object[]) null); + Collection targetCollection = (Collection) implClass.newInstance(); ConversionExecutor elementConverter = getElementConverter(); Class elementType; if (elementConverter == null) { diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/service/GenericConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/service/GenericConversionService.java index 65a38c02775..a699f1fa5ac 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/service/GenericConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/service/GenericConversionService.java @@ -178,7 +178,6 @@ public class GenericConversionService implements ConversionService { throws ConversionExecutorNotFoundException { Assert.notNull(sourceClass, "The sourceType to convert from is required"); Assert.notNull(targetType, "The targetType to convert to is required"); - // special handling for arrays since they are not indexable classes TypeDescriptor sourceType = TypeDescriptor.valueOf(sourceClass); if (sourceType.isArray()) { if (targetType.isArray()) { @@ -206,6 +205,13 @@ public class GenericConversionService implements ConversionService { throw new UnsupportedOperationException("Object to Collection conversion not yet supported"); } } + if (sourceType.isMap()) { + if (targetType.isMap()) { + return new MapToMap(sourceType, targetType, this); + } else { + throw new UnsupportedOperationException("Object to Map conversion not yet supported"); + } + } Converter converter = findRegisteredConverter(sourceType, targetType); if (converter != null) { return new StaticConversionExecutor(sourceType, targetType, converter); diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/service/MapToMap.java b/org.springframework.core/src/main/java/org/springframework/core/convert/service/MapToMap.java new file mode 100644 index 00000000000..b6c6c31e516 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/service/MapToMap.java @@ -0,0 +1,76 @@ +/* + * Copyright 2004-2008 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.service; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.springframework.core.convert.ConversionExecutionException; +import org.springframework.core.convert.ConversionExecutor; +import org.springframework.core.convert.TypeDescriptor; + +class MapToMap implements ConversionExecutor { + + private TypeDescriptor sourceType; + + private TypeDescriptor targetType; + + private GenericConversionService conversionService; + + public MapToMap(TypeDescriptor sourceType, TypeDescriptor targetType, GenericConversionService conversionService) { + this.sourceType = sourceType; + this.targetType = targetType; + this.conversionService = conversionService; + } + + @SuppressWarnings("unchecked") + public Object execute(Object source) throws ConversionExecutionException { + try { + Map map = (Map) source; + Map targetMap = (Map) getImpl(targetType.getType()).newInstance(); + Iterator> it = map.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + Object key = entry.getKey(); + Object value = entry.getValue(); + key = conversionService.executeConversion(key, TypeDescriptor.valueOf(targetType.getMapKeyType())); + value = conversionService.executeConversion(value, TypeDescriptor.valueOf(targetType.getMapValueType())); + targetMap.put(key, value); + } + return targetMap; + } catch (Exception e) { + throw new ConversionExecutionException(source, sourceType, targetType, e); + } + } + + static Class getImpl(Class targetClass) { + if (targetClass.isInterface()) { + if (Map.class.equals(targetClass)) { + return HashMap.class; + } else if (SortedMap.class.equals(targetClass)) { + return TreeMap.class; + } else { + throw new IllegalArgumentException("Unsupported Map interface [" + targetClass.getName() + "]"); + } + } else { + return targetClass; + } + } + +} diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/service/GenericConversionServiceTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/service/GenericConversionServiceTests.java index fb206b4621a..68e3ef76cff 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/convert/service/GenericConversionServiceTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/convert/service/GenericConversionServiceTests.java @@ -15,14 +15,19 @@ */ package org.springframework.core.convert.service; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertSame; +import static junit.framework.Assert.fail; + import java.security.Principal; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; - -import junit.framework.TestCase; +import java.util.Map; import org.junit.Ignore; import org.junit.Test; @@ -36,34 +41,39 @@ import org.springframework.core.convert.converter.NumberToNumber; import org.springframework.core.convert.converter.StringToEnum; import org.springframework.core.convert.converter.StringToInteger; -public class GenericConversionServiceTests extends TestCase { +public class GenericConversionServiceTests { private GenericConversionService service = new GenericConversionService(); - public void testExecuteConversion() { + @Test + public void executeConversion() { service.addConverter(new StringToInteger()); assertEquals(new Integer(3), service.executeConversion("3", type(Integer.class))); } - - public void testExecuteConversionNullSource() { + + @Test + public void executeConversionNullSource() { assertEquals(null, service.executeConversion(null, type(Integer.class))); } - public void testConverterConversionForwardIndex() { + @Test + public void converterConvertForwardIndex() { service.addConverter(new StringToInteger()); ConversionExecutor executor = service.getConversionExecutor(String.class, type(Integer.class)); Integer three = (Integer) executor.execute("3"); assertEquals(3, three.intValue()); } - public void testConverterConversionReverseIndex() { + @Test + public void convertReverseIndex() { service.addConverter(new StringToInteger()); ConversionExecutor executor = service.getConversionExecutor(Integer.class, type(String.class)); String threeString = (String) executor.execute(new Integer(3)); assertEquals("3", threeString); } - public void testConversionExecutorNotFound() { + @Test + public void convertExecutorNotFound() { try { service.getConversionExecutor(String.class, type(Integer.class)); fail("Should have thrown an exception"); @@ -71,7 +81,8 @@ public class GenericConversionServiceTests extends TestCase { } } - public void testAddConverterNoSourceTargetClassInfoAvailable() { + @Test + public void addConverterNoSourceTargetClassInfoAvailable() { try { service.addConverter(new Converter() { public Object convert(Object source) throws Exception { @@ -88,18 +99,21 @@ public class GenericConversionServiceTests extends TestCase { } } - public void testConversionCompatibleTypes() { + @Test + public void convertCompatibleTypes() { String source = "foo"; assertSame(source, service.getConversionExecutor(String.class, type(String.class)).execute(source)); } - public void testConversionExecutorNullArgument() { + @Test + public void convertNull() { service.addConverter(new StringToInteger()); ConversionExecutor executor = service.getConversionExecutor(String.class, type(Integer.class)); assertNull(executor.execute(null)); } - public void testConversionExecutorWrongTypeArgument() { + @Test + public void convertWrongTypeArgument() { service.addConverter(new StringToInteger()); ConversionExecutor executor = service.getConversionExecutor(Integer.class, type(String.class)); try { @@ -110,7 +124,8 @@ public class GenericConversionServiceTests extends TestCase { } } - public void testConverterConversionSuperSourceType() { + @Test + public void convertSuperSourceType() { service.addConverter(new Converter() { public Integer convert(CharSequence source) throws Exception { return Integer.valueOf(source.toString()); @@ -125,7 +140,8 @@ public class GenericConversionServiceTests extends TestCase { assertEquals(new Integer(3), result); } - public void testConverterConversionNoSuperTargetType() { + @Test + public void convertNoSuperTargetType() { service.addConverter(new Converter() { public Integer convert(CharSequence source) throws Exception { return Integer.valueOf(source.toString()); @@ -143,14 +159,16 @@ public class GenericConversionServiceTests extends TestCase { } } - public void testConversionObjectToPrimitive() { + @Test + public void convertObjectToPrimitive() { service.addConverter(new StringToInteger()); ConversionExecutor executor = service.getConversionExecutor(String.class, type(int.class)); Integer three = (Integer) executor.execute("3"); assertEquals(3, three.intValue()); } - public void testConversionArrayToArray() { + @Test + public void convertArrayToArray() { service.addConverter(new StringToInteger()); ConversionExecutor executor = service.getConversionExecutor(String[].class, type(Integer[].class)); Integer[] result = (Integer[]) executor.execute(new String[] { "1", "2", "3" }); @@ -159,7 +177,8 @@ public class GenericConversionServiceTests extends TestCase { assertEquals(new Integer(3), result[2]); } - public void testConversionArrayToPrimitiveArray() { + @Test + public void convertArrayToPrimitiveArray() { service.addConverter(new StringToInteger()); ConversionExecutor executor = service.getConversionExecutor(String[].class, type(int[].class)); int[] result = (int[]) executor.execute(new String[] { "1", "2", "3" }); @@ -168,7 +187,8 @@ public class GenericConversionServiceTests extends TestCase { assertEquals(3, result[2]); } - public void testConversionArrayToListInterface() { + @Test + public void convertArrayToListInterface() { ConversionExecutor executor = service.getConversionExecutor(String[].class, type(List.class)); List result = (List) executor.execute(new String[] { "1", "2", "3" }); assertEquals("1", result.get(0)); @@ -177,17 +197,20 @@ public class GenericConversionServiceTests extends TestCase { } public List genericList = new ArrayList(); - - public void testConversionArrayToListGenericTypeConversion() throws Exception { + + @Test + public void convertArrayToListGenericTypeConversion() throws Exception { service.addConverter(new StringToInteger()); - ConversionExecutor executor = service.getConversionExecutor(String[].class, new TypeDescriptor(getClass().getDeclaredField("genericList"))); + ConversionExecutor executor = service.getConversionExecutor(String[].class, new TypeDescriptor(getClass() + .getDeclaredField("genericList"))); List result = (List) executor.execute(new String[] { "1", "2", "3" }); assertEquals(new Integer("1"), result.get(0)); assertEquals(new Integer("2"), result.get(1)); assertEquals(new Integer("3"), result.get(2)); } - - public void testConversionArrayToListImpl() { + + @Test + public void convertArrayToListImpl() { ConversionExecutor executor = service.getConversionExecutor(String[].class, type(LinkedList.class)); LinkedList result = (LinkedList) executor.execute(new String[] { "1", "2", "3" }); assertEquals("1", result.get(0)); @@ -195,7 +218,8 @@ public class GenericConversionServiceTests extends TestCase { assertEquals("3", result.get(2)); } - public void testConversionArrayToAbstractList() { + @Test + public void convertArrayToAbstractList() { try { service.getConversionExecutor(String[].class, type(AbstractList.class)); } catch (IllegalArgumentException e) { @@ -203,7 +227,8 @@ public class GenericConversionServiceTests extends TestCase { } } - public void testConversionListToArray() { + @Test + public void convertListToArray() { ConversionExecutor executor = service.getConversionExecutor(Collection.class, type(String[].class)); List list = new ArrayList(); list.add("1"); @@ -215,7 +240,8 @@ public class GenericConversionServiceTests extends TestCase { assertEquals("3", result[2]); } - public void testConversionListToArrayWithComponentConversion() { + @Test + public void convertListToArrayWithComponentConversion() { service.addConverter(new StringToInteger()); ConversionExecutor executor = service.getConversionExecutor(Collection.class, type(Integer[].class)); List list = new ArrayList(); @@ -228,9 +254,21 @@ public class GenericConversionServiceTests extends TestCase { assertEquals(new Integer(3), result[2]); } + public Map genericMap = new HashMap(); + + @Test + public void convertMapToMap() throws Exception { + Map foo = new HashMap(); + foo.put("1", "BAR"); + foo.put("2", "BAZ"); + service.addConverter(new StringToInteger()); + service.addConverter(new StringToEnum()); + service.executeConversion(foo, new TypeDescriptor(getClass().getField("genericMap"))); + } + @Ignore @Test - public void conversionObjectToArray() { + public void convertObjectToArray() { ConversionExecutor executor = service.getConversionExecutor(String.class, type(String[].class)); String[] result = (String[]) executor.execute("1,2,3"); assertEquals(1, result.length); @@ -238,8 +276,8 @@ public class GenericConversionServiceTests extends TestCase { } @Ignore - @Test - public void conversionObjectToArrayWithElementConversion() { + @Test + public void convertObjectToArrayWithElementConversion() { service.addConverter(new StringToInteger()); ConversionExecutor executor = service.getConversionExecutor(String.class, type(Integer[].class)); Integer[] result = (Integer[]) executor.execute("123"); @@ -248,22 +286,25 @@ public class GenericConversionServiceTests extends TestCase { } public static enum FooEnum { - BAR + BAR, BAZ } - public void testSuperConverterConversionForwardIndex() { + @Test + public void superConverterConvertForwardIndex() { service.addConverter(new StringToEnum()); ConversionExecutor executor = service.getConversionExecutor(String.class, type(FooEnum.class)); assertEquals(FooEnum.BAR, executor.execute("BAR")); } - public void testSuperTwoWayConverterConversionReverseIndex() { + @Test + public void superTwoWayConverterConvertReverseIndex() { service.addConverter(new StringToEnum()); ConversionExecutor executor = service.getConversionExecutor(FooEnum.class, type(String.class)); assertEquals("BAR", executor.execute(FooEnum.BAR)); } - public void testSuperConverterConversionNotConvertibleAbstractType() { + @Test + public void superConverterConvertNotConvertibleAbstractType() { service.addConverter(new StringToEnum()); ConversionExecutor executor = service.getConversionExecutor(String.class, type(Enum.class)); try { @@ -274,7 +315,8 @@ public class GenericConversionServiceTests extends TestCase { } } - public void testSuperConverterConversionNotConvertibleAbstractType2() { + @Test + public void superConverterConvertNotConvertibleAbstractType2() { service.addConverter(new NumberToNumber()); Number customNumber = new Number() { @Override @@ -308,7 +350,7 @@ public class GenericConversionServiceTests extends TestCase { @Ignore @Test - public void customConverterConversionForwardIndex() { + public void customConverterConvertForwardIndex() { service.addConverter("princy", new CustomTwoWayConverter()); ConversionExecutor executor = service.getConversionExecutor("princy", String.class, type(Principal.class)); assertEquals("keith", ((Principal) executor.execute("keith")).getName()); @@ -316,7 +358,7 @@ public class GenericConversionServiceTests extends TestCase { @Ignore @Test - public void customConverterConversionReverseIndex() { + public void customConverterConvertReverseIndex() { service.addConverter("princy", new CustomTwoWayConverter()); ConversionExecutor executor = service.getConversionExecutor("princy", Principal.class, type(String.class)); assertEquals("keith", executor.execute(new Principal() { @@ -328,7 +370,7 @@ public class GenericConversionServiceTests extends TestCase { @Ignore @Test - public void customConverterConversionForSameType() { + public void customConverterConvertForSameType() { service.addConverter("trimmer", new Trimmer()); ConversionExecutor executor = service.getConversionExecutor("trimmer", String.class, type(String.class)); assertEquals("a string", executor.execute("a string ")); @@ -370,7 +412,7 @@ public class GenericConversionServiceTests extends TestCase { @Ignore @Test - public void customConverterConversionArrayToArray() { + public void customConverterConvertArrayToArray() { service.addConverter("princy", new CustomTwoWayConverter()); ConversionExecutor executor = service.getConversionExecutor("princy", String[].class, type(Principal[].class)); Principal[] p = (Principal[]) executor.execute(new String[] { "princy1", "princy2" }); @@ -380,7 +422,7 @@ public class GenericConversionServiceTests extends TestCase { @Ignore @Test - public void customConverterConversionArrayToArrayReverse() { + public void customConverterConvertArrayToArrayReverse() { service.addConverter("princy", new CustomTwoWayConverter()); ConversionExecutor executor = service.getConversionExecutor("princy", Principal[].class, type(String[].class)); final Principal princy1 = new Principal() { @@ -422,7 +464,7 @@ public class GenericConversionServiceTests extends TestCase { @Ignore @Test - public void customConverterConversionArrayToCollection() { + public void customConverterConvertArrayToCollection() { service.addConverter("princy", new CustomTwoWayConverter()); ConversionExecutor executor = service.getConversionExecutor("princy", String[].class, type(List.class)); List list = (List) executor.execute(new String[] { "princy1", "princy2" }); @@ -432,7 +474,7 @@ public class GenericConversionServiceTests extends TestCase { @Ignore @Test - public void customConverterConversionArrayToCollectionReverse() { + public void customConverterConvertArrayToCollectionReverse() { service.addConverter("princy", new CustomTwoWayConverter()); ConversionExecutor executor = service.getConversionExecutor("princy", Principal[].class, type(List.class)); final Principal princy1 = new Principal() { @@ -512,7 +554,7 @@ public class GenericConversionServiceTests extends TestCase { @Ignore @Test - public void customConverterConversionObjectToArray() { + public void customConverterConvertObjectToArray() { service.addConverter("princy", new CustomTwoWayConverter()); ConversionExecutor executor = service.getConversionExecutor("princy", String.class, type(Principal[].class)); Principal[] p = (Principal[]) executor.execute("princy1"); @@ -520,8 +562,8 @@ public class GenericConversionServiceTests extends TestCase { } @Ignore - @Test - public void customConverterConversionObjectToArrayReverse() { + @Test + public void customConverterConvertObjectToArrayReverse() { service.addConverter("princy", new CustomTwoWayConverter()); ConversionExecutor executor = service.getConversionExecutor("princy", Principal.class, type(String[].class)); final Principal princy1 = new Principal() { @@ -534,7 +576,7 @@ public class GenericConversionServiceTests extends TestCase { } @Ignore - @Test + @Test public void customConverterLookupObjectToArrayBogusSource() { service.addConverter("princy", new CustomTwoWayConverter()); try { @@ -577,9 +619,9 @@ public class GenericConversionServiceTests extends TestCase { service.addConverter(GenericConversionService.converterFor(String.class, FooEnum.class, new StringToEnum())); assertEquals(FooEnum.BAR, service.executeConversion("BAR", type(FooEnum.class))); } - + private TypeDescriptor type(Class clazz) { return TypeDescriptor.valueOf(clazz); } - + } \ No newline at end of file