diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToCollection.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToCollection.java index 140f7c63..fe0ad93f 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToCollection.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToCollection.java @@ -95,8 +95,13 @@ public class ArrayToCollection implements TwoWayConverter { for (Iterator it = collection.iterator(); it.hasNext(); i++) { Object value = it.next(); if (value != null) { - ConversionExecutor converter = conversionService.getConversionExecutor(value.getClass(), sourceClass - .getComponentType()); + ConversionExecutor converter; + if (elementConverter != null) { + converter = elementConverter; + } else { + converter = conversionService.getConversionExecutor(value.getClass(), sourceClass + .getComponentType()); + } value = converter.execute(value); } Array.set(array, i, value); diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ObjectToArray.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ObjectToArray.java index d3b1f45d..3136f0d4 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ObjectToArray.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ObjectToArray.java @@ -30,10 +30,16 @@ public class ObjectToArray implements Converter { private ConversionService conversionService; + private ConversionExecutor elementConverter; + public ObjectToArray(ConversionService conversionService) { this.conversionService = conversionService; } + public ObjectToArray(ConversionExecutor elementConverter) { + this.elementConverter = elementConverter; + } + public Class getSourceClass() { return Object.class; } @@ -48,7 +54,12 @@ public class ObjectToArray implements Converter { } Class componentType = targetClass.getComponentType(); Object array = Array.newInstance(componentType, 1); - ConversionExecutor converter = conversionService.getConversionExecutor(source.getClass(), componentType); + ConversionExecutor converter; + if (elementConverter != null) { + converter = elementConverter; + } else { + converter = conversionService.getConversionExecutor(source.getClass(), componentType); + } Array.set(array, 0, converter.execute(source)); return array; } diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java index c8ab4e9a..361bea32 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java @@ -191,8 +191,11 @@ public class GenericConversionService implements ConversionService { if (converter.getSourceClass().isAssignableFrom(sourceComponentType)) { if (!converter.getTargetClass().isAssignableFrom(targetComponentType)) { throw new ConversionExecutorNotFoundException(sourceClass, targetClass, - "Custom ConversionExecutor with id '" + id + "' cannot convert from array of type [" - + sourceComponentType + "]; to an array of type [" + targetComponentType + "]"); + "Custom ConversionExecutor with id '" + id + + "' cannot convert from array storing elements of type [" + + sourceComponentType.getName() + + "]; to an array of storing elements of type [" + + targetComponentType.getName() + "]"); } ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType, targetComponentType, converter); @@ -206,42 +209,82 @@ public class GenericConversionService implements ConversionService { } else { throw new ConversionExecutorNotFoundException(sourceClass, targetClass, "Custom ConversionExecutor with id '" + id + "' cannot convert from array of type [" - + sourceComponentType + "]; to an array of type [" + targetComponentType + "]"); + + sourceComponentType.getName() + "]; to an array of type [" + + targetComponentType.getName() + "]"); } } else if (Collection.class.isAssignableFrom(targetClass)) { - // type erasure has prevented us from getting the concrete type, this is best we can do for now - Class targetComponentType = converter.getTargetClass(); + if (!targetClass.isInterface() && Modifier.isAbstract(targetClass.getModifiers())) { + throw new IllegalArgumentException("Conversion target class [" + targetClass.getName() + + "] is invalid; cannot convert to abstract collection types--" + + "request an interface or concrete implementation instead"); + } if (converter.getSourceClass().isAssignableFrom(sourceComponentType)) { - if (!converter.getTargetClass().isAssignableFrom(targetComponentType)) { - throw new ConversionExecutorNotFoundException(sourceClass, targetClass, - "Custom ConversionExecutor with id '" + id + "' cannot convert from array of type [" - + sourceComponentType + "]; to collection of type [" + targetComponentType - + "]"); - } - ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType, - targetComponentType, converter); + // type erasure has prevented us from getting the concrete type, this is best we can do for now + ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType, converter + .getTargetClass(), converter); return new StaticConversionExecutor(sourceClass, targetClass, new ArrayToCollection( elementConverter)); } else if (converter.getTargetClass().isAssignableFrom(sourceComponentType) && converter instanceof TwoWayConverter) { TwoWayConverter twoWay = (TwoWayConverter) converter; - ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType, - targetComponentType, new ReverseConverter(twoWay)); + ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType, converter + .getSourceClass(), new ReverseConverter(twoWay)); return new StaticConversionExecutor(sourceClass, targetClass, new ArrayToCollection( elementConverter)); } else { throw new ConversionExecutorNotFoundException(sourceClass, targetClass, - "Custom ConversionExecutor with id '" + id + "' cannot convert from array of type [" - + sourceComponentType + "]; to collection of type [" + targetComponentType + "]"); + "Custom ConversionExecutor with id '" + id + + "' cannot convert from array storing elements type [" + + sourceComponentType.getName() + "]; to collection"); } } } if (targetClass.isArray()) { + Class targetComponentType = targetClass.getComponentType(); if (Collection.class.isAssignableFrom(sourceClass)) { // type erasure limits us here as well - throw new UnsupportedOperationException("Not yet implemented"); + if (converter.getTargetClass().isAssignableFrom(targetComponentType)) { + ConversionExecutor elementConverter = new StaticConversionExecutor(converter.getSourceClass(), + targetComponentType, converter); + Converter collectionToArray = new ReverseConverter(new ArrayToCollection(elementConverter)); + return new StaticConversionExecutor(sourceClass, targetClass, collectionToArray); + } else if (converter.getSourceClass().isAssignableFrom(targetComponentType) + && converter instanceof TwoWayConverter) { + TwoWayConverter twoWay = (TwoWayConverter) converter; + ConversionExecutor elementConverter = new StaticConversionExecutor(converter.getTargetClass(), + targetComponentType, new ReverseConverter(twoWay)); + Converter collectionToArray = new ReverseConverter(new ArrayToCollection(elementConverter)); + return new StaticConversionExecutor(sourceClass, targetClass, collectionToArray); + } else { + throw new ConversionExecutorNotFoundException(sourceClass, targetClass, + "Custom ConversionExecutor with id '" + id + + "' cannot convert from collection to to an array holding elements of type [" + + targetComponentType.getName() + "]"); + } } else { - throw new UnsupportedOperationException("Not yet implemented"); + if (converter.getSourceClass().isAssignableFrom(sourceClass)) { + if (!converter.getTargetClass().isAssignableFrom(targetComponentType)) { + throw new ConversionExecutorNotFoundException(sourceClass, targetClass, + "Custom ConversionExecutor with id '" + id + "' cannot convert from sourceClass [" + + sourceClass.getName() + "] to array holding elements of type [" + + targetComponentType.getName() + "]"); + } + ConversionExecutor elementConverter = new StaticConversionExecutor(sourceClass, + targetComponentType, converter); + return new StaticConversionExecutor(sourceClass, targetClass, new ObjectToArray(elementConverter)); + } else if (converter.getTargetClass().isAssignableFrom(sourceClass) + && converter instanceof TwoWayConverter) { + if (!converter.getSourceClass().isAssignableFrom(targetComponentType)) { + throw new ConversionExecutorNotFoundException(sourceClass, targetClass, + "Custom ConversionExecutor with id '" + id + "' cannot convert from sourceClass [" + + sourceClass.getName() + "] to array holding elements of type [" + + targetComponentType.getName() + "]"); + } + TwoWayConverter twoWay = (TwoWayConverter) converter; + ConversionExecutor elementConverter = new StaticConversionExecutor(sourceClass, + targetComponentType, new ReverseConverter(twoWay)); + return new StaticConversionExecutor(sourceClass, targetClass, new ObjectToArray(elementConverter)); + } } } if (converter.getSourceClass().isAssignableFrom(sourceClass)) { diff --git a/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java b/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java index ff7a4b61..6d53813d 100644 --- a/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java @@ -197,6 +197,89 @@ public class DefaultConversionServiceTests extends TestCase { assertEquals("princy2", p[1]); } + public void testRegisterCustomConverterArrayToList() { + DefaultConversionService service = new DefaultConversionService(); + service.addConverter("princy", new CustomTwoWayConverter()); + ConversionExecutor executor = service.getConversionExecutor("princy", String[].class, List.class); + List list = (List) executor.execute(new String[] { "princy1", "princy2" }); + assertEquals("princy1", ((Principal) list.get(0)).getName()); + assertEquals("princy2", ((Principal) list.get(1)).getName()); + } + + public void testRegisterCustomConverterArrayToListReverse() { + DefaultConversionService service = new DefaultConversionService(); + service.addConverter("princy", new CustomTwoWayConverter()); + ConversionExecutor executor = service.getConversionExecutor("princy", Principal[].class, List.class); + final Principal princy1 = new Principal() { + public String getName() { + return "princy1"; + } + }; + final Principal princy2 = new Principal() { + public String getName() { + return "princy2"; + } + }; + List p = (List) executor.execute(new Principal[] { princy1, princy2 }); + assertEquals("princy1", p.get(0)); + assertEquals("princy2", p.get(1)); + } + + public void testRegisterCustomConverterListToArray() { + DefaultConversionService service = new DefaultConversionService(); + service.addConverter("princy", new CustomTwoWayConverter()); + ConversionExecutor executor = service.getConversionExecutor("princy", List.class, Principal[].class); + List princyList = new ArrayList(); + princyList.add("princy1"); + princyList.add("princy2"); + Principal[] p = (Principal[]) executor.execute(princyList); + assertEquals("princy1", p[0].getName()); + assertEquals("princy2", p[1].getName()); + } + + public void testRegisterCustomConverterListToArrayReverse() { + DefaultConversionService service = new DefaultConversionService(); + service.addConverter("princy", new CustomTwoWayConverter()); + ConversionExecutor executor = service.getConversionExecutor("princy", List.class, String[].class); + final Principal princy1 = new Principal() { + public String getName() { + return "princy1"; + } + }; + final Principal princy2 = new Principal() { + public String getName() { + return "princy2"; + } + }; + List princyList = new ArrayList(); + princyList.add(princy1); + princyList.add(princy2); + String[] p = (String[]) executor.execute(princyList); + assertEquals("princy1", p[0]); + assertEquals("princy2", p[1]); + } + + public void testRegisterCustomConverterObjectToArray() { + DefaultConversionService service = new DefaultConversionService(); + service.addConverter("princy", new CustomTwoWayConverter()); + ConversionExecutor executor = service.getConversionExecutor("princy", String.class, Principal[].class); + Principal[] p = (Principal[]) executor.execute("princy1"); + assertEquals("princy1", p[0].getName()); + } + + public void testRegisterCustomConverterObjectToArrayReverse() { + DefaultConversionService service = new DefaultConversionService(); + service.addConverter("princy", new CustomTwoWayConverter()); + ConversionExecutor executor = service.getConversionExecutor("princy", Principal.class, String[].class); + final Principal princy1 = new Principal() { + public String getName() { + return "princy1"; + } + }; + String[] p = (String[]) executor.execute(princy1); + assertEquals("princy1", p[0]); + } + public void testConversionPrimitive() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String.class, int.class);