diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/ConversionExecutor.java b/spring-binding/src/main/java/org/springframework/binding/convert/ConversionExecutor.java index 37eb8872..ba171f53 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/ConversionExecutor.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/ConversionExecutor.java @@ -15,7 +15,6 @@ */ package org.springframework.binding.convert; - /** * A command object that is parameterized with the information necessary to perform a conversion of a source input to a * target output. Encapsulates knowledge about how to convert source objects to a specific target type using a specific 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 374af6f1..73672df0 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 @@ -64,46 +64,17 @@ public class ArrayToCollection implements TwoWayConverter { if (source == null) { return null; } - Class collectionImplClass; - if (targetClass.isInterface()) { - if (List.class.equals(targetClass)) { - collectionImplClass = ArrayList.class; - } else if (Set.class.equals(targetClass)) { - collectionImplClass = LinkedHashSet.class; - } else if (SortedSet.class.equals(targetClass)) { - collectionImplClass = TreeSet.class; - } else { - throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]"); - } - } else { - collectionImplClass = targetClass; - } + Class collectionImplClass = getCollectionImplClass(targetClass); Constructor constructor = collectionImplClass.getConstructor(null); - ConversionExecutor converter; - if (JdkVersion.isAtLeastJava15()) { - Class elementType = GenericCollectionTypeResolver.getCollectionType(targetClass); - if (elementType != null) { - Class componentType = source.getClass().getComponentType(); - converter = conversionService.getConversionExecutor(componentType, elementType); - } else { - converter = null; - } - } else { - converter = null; - } Collection collection = (Collection) constructor.newInstance(null); + ConversionExecutor converter = getElementConverter(source, targetClass); int length = Array.getLength(source); - if (converter != null) { - for (int i = 0; i < length; i++) { - Object value = Array.get(source, i); + for (int i = 0; i < length; i++) { + Object value = Array.get(source, i); + if (converter != null) { value = converter.execute(value); - collection.add(value); - } - } else { - for (int i = 0; i < length; i++) { - Object value = Array.get(source, i); - collection.add(value); } + collection.add(value); } return collection; } @@ -126,4 +97,32 @@ public class ArrayToCollection implements TwoWayConverter { } return array; } -} + + private Class getCollectionImplClass(Class targetClass) { + if (targetClass.isInterface()) { + if (List.class.equals(targetClass)) { + return ArrayList.class; + } else if (Set.class.equals(targetClass)) { + return LinkedHashSet.class; + } else if (SortedSet.class.equals(targetClass)) { + return TreeSet.class; + } else { + throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]"); + } + } else { + return targetClass; + } + } + + 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); + } + } + return null; + } + +} \ No newline at end of file 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 9bbcd310..d3b1f45d 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 @@ -21,13 +21,12 @@ import org.springframework.binding.convert.ConversionExecutor; import org.springframework.binding.convert.ConversionService; /** - * Special two-way converter that converts an object to an single-element array. Supports type conversion of the - * individual array elements; for example, the ability to convert a String to an Integer[]. Mainly used internally by + * Special two-way converter that converts an object to an single-element array. Mainly used internally by * {@link ConversionService} implementations. * * @author Keith Donald */ -public class ObjectToArray implements TwoWayConverter { +public class ObjectToArray implements Converter { private ConversionService conversionService; @@ -47,50 +46,10 @@ public class ObjectToArray implements TwoWayConverter { if (source == null) { return null; } - if (source instanceof String) { - String string = (String) source; - String[] elements = string.split(","); - Class componentType = targetClass.getComponentType(); - Object array = Array.newInstance(componentType, elements.length); - ConversionExecutor converter = conversionService.getConversionExecutor(String.class, componentType); - for (int i = 0; i < elements.length; i++) { - String element = elements[i].trim(); - Array.set(array, i, converter.execute(element)); - } - return array; - } else { - Class componentType = targetClass.getComponentType(); - Object array = Array.newInstance(componentType, 1); - ConversionExecutor converter = conversionService.getConversionExecutor(source.getClass(), componentType); - Array.set(array, 0, converter.execute(source)); - return array; - } + Class componentType = targetClass.getComponentType(); + Object array = Array.newInstance(componentType, 1); + ConversionExecutor converter = conversionService.getConversionExecutor(source.getClass(), componentType); + Array.set(array, 0, converter.execute(source)); + return array; } - - public Object convertTargetToSourceClass(Object target, Class sourceClass) throws Exception { - if (target == null) { - return null; - } - if (String.class.equals(sourceClass)) { - int length = Array.getLength(target); - Class componentType = target.getClass().getComponentType(); - ConversionExecutor converter = conversionService.getConversionExecutor(componentType, String.class); - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < length; i++) { - Object value = Array.get(target, i); - if (value != null) { - buffer.append(converter.execute(value)); - } - if (i < length - 1) { - buffer.append(","); - } - } - return buffer.toString(); - } else { - Object value = Array.get(target, 0); - Class componentType = target.getClass().getComponentType(); - ConversionExecutor converter = conversionService.getConversionExecutor(componentType, sourceClass); - return converter.execute(value); - } - } -} +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ObjectToCollection.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ObjectToCollection.java new file mode 100644 index 00000000..bb8b7870 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ObjectToCollection.java @@ -0,0 +1,98 @@ +/* + * 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.binding.convert.converters; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.springframework.binding.convert.ConversionExecutor; +import org.springframework.binding.convert.ConversionService; +import org.springframework.core.GenericCollectionTypeResolver; +import org.springframework.core.JdkVersion; + +/** + * Special two-way converter that converts an object to an single-element collection. Supports type conversion of the + * individual element with parameterized collection implementations. + * + * @author Keith Donald + */ +public class ObjectToCollection implements Converter { + + private ConversionService conversionService; + + public ObjectToCollection(ConversionService conversionService) { + this.conversionService = conversionService; + } + + public Class getSourceClass() { + return Object.class; + } + + public Class getTargetClass() { + return Collection.class; + } + + public Object convertSourceToTargetClass(Object source, Class targetClass) throws Exception { + if (source == null) { + return null; + } + Class collectionImplClass = getCollectionImplClass(targetClass); + Constructor constructor = collectionImplClass.getConstructor(null); + Collection collection = (Collection) constructor.newInstance(null); + ConversionExecutor converter = getElementConverter(source, targetClass); + Object value; + if (converter != null) { + value = converter.execute(source); + } else { + value = source; + } + collection.add(value); + return collection; + } + + private Class getCollectionImplClass(Class targetClass) { + if (targetClass.isInterface()) { + if (List.class.equals(targetClass)) { + return ArrayList.class; + } else if (Set.class.equals(targetClass)) { + return LinkedHashSet.class; + } else if (SortedSet.class.equals(targetClass)) { + return TreeSet.class; + } else { + throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]"); + } + } else { + return targetClass; + } + } + + 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); + } + } + return null; + } +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java index 16aea55e..8f7c4124 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java @@ -20,6 +20,7 @@ import java.math.BigInteger; import java.util.Date; import java.util.Locale; +import org.springframework.binding.convert.converters.ObjectToCollection; import org.springframework.binding.convert.converters.StringToBigDecimal; import org.springframework.binding.convert.converters.StringToBigInteger; import org.springframework.binding.convert.converters.StringToBoolean; @@ -68,6 +69,7 @@ public class DefaultConversionService extends GenericConversionService { addConverter(new StringToLocale()); addConverter(new StringToDate()); addConverter(new StringToLabeledEnum()); + addConverter(new ObjectToCollection(this)); } protected void addDefaultAliases() { 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 3157a158..a72caeff 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 @@ -134,9 +134,6 @@ public class GenericConversionService implements ConversionService { + "request an interface or concrete implementation instead"); } return new StaticConversionExecutor(sourceClass, targetClass, new ArrayToCollection(this)); - } else { - Converter arrayToObject = new ReverseConverter(new ObjectToArray(this)); - return new StaticConversionExecutor(sourceClass, targetClass, arrayToObject); } } if (targetClass.isArray()) { @@ -147,8 +144,7 @@ public class GenericConversionService implements ConversionService { return new StaticConversionExecutor(sourceClass, targetClass, new ObjectToArray(this)); } } - Map sourceTargetConverters = findConvertersForSource(sourceClass); - Converter converter = findTargetConverter(sourceTargetConverters, targetClass); + Converter converter = findRegisteredConverter(sourceClass, targetClass); if (converter != null) { // we found a converter return new StaticConversionExecutor(sourceClass, targetClass, converter); @@ -164,6 +160,27 @@ public class GenericConversionService implements ConversionService { } } + private Converter findRegisteredConverter(Class sourceClass, Class targetClass) { + LinkedList classQueue = new LinkedList(); + classQueue.addFirst(sourceClass); + while (!classQueue.isEmpty()) { + Class currentClass = (Class) classQueue.removeLast(); + Map sourceTargetConverters = findConvertersForSource(currentClass); + Converter converter = findTargetConverter(sourceTargetConverters, targetClass); + if (converter != null) { + return converter; + } + if (currentClass.getSuperclass() != null) { + classQueue.addFirst(currentClass.getSuperclass()); + } + Class[] interfaces = currentClass.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + classQueue.addFirst(interfaces[i]); + } + } + return null; + } + public Object executeConversion(Object source, Class targetClass) throws ConversionException { if (source != null) { ConversionExecutor conversionExecutor = getConversionExecutor(source.getClass(), targetClass); @@ -226,47 +243,8 @@ public class GenericConversionService implements ConversionService { // internal helpers private Map findConvertersForSource(Class sourceClass) { - if (sourceClass.isInterface()) { - LinkedList classQueue = new LinkedList(); - classQueue.addFirst(sourceClass); - while (!classQueue.isEmpty()) { - sourceClass = (Class) classQueue.removeLast(); - Map sourceTargetConverters = (Map) sourceClassConverters.get(sourceClass); - if (sourceTargetConverters != null && !sourceTargetConverters.isEmpty()) { - return sourceTargetConverters; - } - // queue up source class's implemented interfaces. - Class[] interfaces = sourceClass.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - classQueue.addFirst(interfaces[i]); - } - } - Map sourceTargetConverters = (Map) sourceClassConverters.get(Object.class); - if (sourceTargetConverters != null && !sourceTargetConverters.isEmpty()) { - return sourceTargetConverters; - } else { - return Collections.EMPTY_MAP; - } - } else { - LinkedList classQueue = new LinkedList(); - classQueue.addFirst(sourceClass); - while (!classQueue.isEmpty()) { - sourceClass = (Class) classQueue.removeLast(); - Map sourceTargetConverters = (Map) sourceClassConverters.get(sourceClass); - if (sourceTargetConverters != null && !sourceTargetConverters.isEmpty()) { - return sourceTargetConverters; - } - if (sourceClass.getSuperclass() != null) { - classQueue.addFirst(sourceClass.getSuperclass()); - } - // queue up source class's implemented interfaces. - Class[] interfaces = sourceClass.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - classQueue.addFirst(interfaces[i]); - } - } - return Collections.EMPTY_MAP; - } + Map sourceConverters = (Map) sourceClassConverters.get(sourceClass); + return sourceConverters != null ? sourceConverters : Collections.EMPTY_MAP; } private Converter findTargetConverter(Map sourceTargetConverters, Class 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 024a5db0..d2536f2e 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 @@ -187,25 +187,24 @@ public class DefaultConversionServiceTests extends TestCase { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String.class, String[].class); String[] result = (String[]) executor.execute("1,2,3"); - assertEquals("1", result[0]); - assertEquals("2", result[1]); - assertEquals("3", result[2]); + assertEquals(1, result.length); + assertEquals("1,2,3", result[0]); + } + + public void testToListConversion() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(String.class, List.class); + List result = (List) executor.execute("1,2,3"); + assertEquals(1, result.size()); + assertEquals("1,2,3", result.get(0)); } public void testToArrayConversionWithElementConversion() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String.class, Integer[].class); - Integer[] result = (Integer[]) executor.execute("1,2,3"); - assertEquals(new Integer(1), result[0]); - assertEquals(new Integer(2), result[1]); - assertEquals(new Integer(3), result[2]); - } - - public void testToArrayObjectConversion() { - DefaultConversionService service = new DefaultConversionService(); - ConversionExecutor executor = service.getConversionExecutor(String[].class, String.class); - String result = (String) executor.execute(new String[] { "1", "2", "3" }); - assertEquals("1,2,3", result); + Integer[] result = (Integer[]) executor.execute("123"); + assertEquals(1, result.length); + assertEquals(new Integer(123), result[0]); } // public void testGenericTypeConversionOGNL() {