diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/CollectionToCollection.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/CollectionToCollection.java index 6ea8ea34..9e0dee0e 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/converters/CollectionToCollection.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/CollectionToCollection.java @@ -18,10 +18,16 @@ public class CollectionToCollection implements Converter { private ConversionService conversionService; + private ConversionExecutor elementConverter; + public CollectionToCollection(ConversionService conversionService) { this.conversionService = conversionService; } + public CollectionToCollection(ConversionExecutor elementConverter) { + this.elementConverter = elementConverter; + } + public Class getSourceClass() { return Collection.class; } @@ -66,14 +72,18 @@ public class CollectionToCollection implements Converter { } private ConversionExecutor getElementConverter(Object source, Class targetClass) { - if (JdkVersion.isAtLeastJava15()) { - Class elementType = GenericCollectionTypeResolver.getCollectionType(targetClass); - if (elementType != null) { - Class componentType = source.getClass().getComponentType(); - return conversionService.getConversionExecutor(componentType, elementType); + if (elementConverter != null) { + return elementConverter; + } else { + if (JdkVersion.isAtLeastJava15()) { + Class elementType = GenericCollectionTypeResolver.getCollectionType(targetClass); + if (elementType != null) { + Class componentType = source.getClass().getComponentType(); + return conversionService.getConversionExecutor(componentType, elementType); + } } + return null; } - return null; } } \ No newline at end of file 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 361bea32..eb4e337e 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 @@ -26,11 +26,13 @@ import java.util.Map; import java.util.Set; import org.springframework.binding.convert.ConversionException; +import org.springframework.binding.convert.ConversionExecutionException; import org.springframework.binding.convert.ConversionExecutor; import org.springframework.binding.convert.ConversionExecutorNotFoundException; import org.springframework.binding.convert.ConversionService; import org.springframework.binding.convert.converters.ArrayToArray; import org.springframework.binding.convert.converters.ArrayToCollection; +import org.springframework.binding.convert.converters.CollectionToCollection; import org.springframework.binding.convert.converters.Converter; import org.springframework.binding.convert.converters.ObjectToArray; import org.springframework.binding.convert.converters.ReverseConverter; @@ -287,6 +289,18 @@ public class GenericConversionService implements ConversionService { } } } + if (Collection.class.isAssignableFrom(sourceClass) && Collection.class.isAssignableFrom(targetClass)) { + ConversionExecutor elementConverter; + // type erasure forces us to do runtime checks of list elements + if (converter instanceof TwoWayConverter) { + elementConverter = new TwoWayCapableConversionExecutor(converter.getSourceClass(), converter + .getTargetClass(), (TwoWayConverter) converter); + } else { + elementConverter = new StaticConversionExecutor(converter.getSourceClass(), converter.getTargetClass(), + converter); + } + return new StaticConversionExecutor(sourceClass, targetClass, new CollectionToCollection(elementConverter)); + } if (converter.getSourceClass().isAssignableFrom(sourceClass)) { if (!converter.getTargetClass().isAssignableFrom(targetClass)) { throw new ConversionExecutorNotFoundException(sourceClass, targetClass, @@ -488,4 +502,46 @@ public class GenericConversionService implements ConversionService { } } + private static class TwoWayCapableConversionExecutor implements ConversionExecutor { + + private Class sourceClass; + + private Class targetClass; + + private TwoWayConverter converter; + + public TwoWayCapableConversionExecutor(Class sourceClass, Class targetClass, TwoWayConverter converter) { + this.sourceClass = sourceClass; + this.targetClass = targetClass; + this.converter = converter; + } + + public Class getSourceClass() { + return sourceClass; + } + + public Class getTargetClass() { + return targetClass; + } + + public Object execute(Object source) throws ConversionExecutionException { + if (source == null || getSourceClass().isInstance(source)) { + try { + return converter.convertSourceToTargetClass(source, targetClass); + } catch (Exception e) { + throw new ConversionExecutionException(source, getSourceClass(), getTargetClass(), e); + } + } else if (getTargetClass().isInstance(source)) { + try { + return converter.convertTargetToSourceClass(source, sourceClass); + } catch (Exception e) { + throw new ConversionExecutionException(source, getTargetClass(), getSourceClass(), e); + } + } else { + throw new ConversionExecutionException(source, getSourceClass(), getTargetClass(), "Source object " + + source + " to convert is expected to be an instance of [" + getSourceClass().getName() + + "] or [" + getTargetClass().getName() + "]"); + } + } + } } \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/service/StaticConversionExecutor.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/StaticConversionExecutor.java index 09291b17..bea609ad 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/service/StaticConversionExecutor.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/service/StaticConversionExecutor.java @@ -89,7 +89,7 @@ public class StaticConversionExecutor implements ConversionExecutor { public Object execute(Object source) throws ConversionExecutionException { if (source != null && !sourceClass.isInstance(source)) { throw new ConversionExecutionException(source, getSourceClass(), getTargetClass(), "Source object " - + source + " to convert is expected to be an instance of " + getSourceClass()); + + source + " to convert is expected to be an instance of [" + getSourceClass().getName() + "]"); } try { return converter.convertSourceToTargetClass(source, targetClass); 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 6d53813d..019076e6 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 @@ -280,6 +280,40 @@ public class DefaultConversionServiceTests extends TestCase { assertEquals("princy1", p[0]); } + public void testRegisterCustomConverterListToList() { + DefaultConversionService service = new DefaultConversionService(); + service.addConverter("princy", new CustomTwoWayConverter()); + ConversionExecutor executor = service.getConversionExecutor("princy", List.class, List.class); + List princyList = new ArrayList(); + princyList.add("princy1"); + princyList.add("princy2"); + List list = (List) executor.execute(princyList); + assertEquals("princy1", ((Principal) list.get(0)).getName()); + assertEquals("princy2", ((Principal) list.get(1)).getName()); + } + + public void testRegisterCustomConverterListToListReverse() { + DefaultConversionService service = new DefaultConversionService(); + service.addConverter("princy", new CustomTwoWayConverter()); + ConversionExecutor executor = service.getConversionExecutor("princy", List.class, List.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); + List list = (List) executor.execute(princyList); + assertEquals("princy1", list.get(0)); + assertEquals("princy2", list.get(1)); + } + public void testConversionPrimitive() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String.class, int.class);