diff --git a/spring-binding/.settings/org.eclipse.jdt.core.prefs b/spring-binding/.settings/org.eclipse.jdt.core.prefs index 5aafbc8d..f53c1ef9 100644 --- a/spring-binding/.settings/org.eclipse.jdt.core.prefs +++ b/spring-binding/.settings/org.eclipse.jdt.core.prefs @@ -1,4 +1,4 @@ -#Tue Aug 12 14:24:16 EDT 2008 +#Thu Feb 12 13:53:33 EST 2009 eclipse.preferences.version=1 org.eclipse.jdt.core.codeComplete.argumentPrefixes= org.eclipse.jdt.core.codeComplete.argumentSuffixes= @@ -9,22 +9,22 @@ org.eclipse.jdt.core.codeComplete.localSuffixes= org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.4 +org.eclipse.jdt.core.compiler.compliance=1.5 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.doc.comment.support=enabled org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning -org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.autoboxing=ignore org.eclipse.jdt.core.compiler.problem.deprecation=warning org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore -org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning @@ -79,7 +79,7 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=di org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.3 +org.eclipse.jdt.core.compiler.source=1.5 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToArray.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToArray.java index 68ceb3cc..91219df5 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToArray.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToArray.java @@ -31,6 +31,8 @@ public class ArrayToArray implements Converter { private ConversionService conversionService; + private ConversionExecutor elementConverter; + /** * Creates a new array-to-array converter. * @param conversionService the service to use to lookup conversion executors for individual array elements @@ -39,6 +41,10 @@ public class ArrayToArray implements Converter { this.conversionService = conversionService; } + public ArrayToArray(ConversionExecutor elementConverter) { + this.elementConverter = elementConverter; + } + public Class getSourceClass() { return Object[].class; } @@ -55,12 +61,19 @@ public class ArrayToArray implements Converter { Class targetComponentType = targetClass.getComponentType(); int length = Array.getLength(source); Object targetArray = Array.newInstance(targetComponentType, length); - ConversionExecutor converter = conversionService - .getConversionExecutor(sourceComponentType, targetComponentType); + ConversionExecutor converter = getElementConverter(sourceComponentType, targetComponentType); for (int i = 0; i < length; i++) { Object value = Array.get(source, i); Array.set(targetArray, i, converter.execute(value)); } return targetArray; } + + private ConversionExecutor getElementConverter(Class sourceComponentType, Class targetComponentType) { + if (elementConverter != null) { + return elementConverter; + } else { + return conversionService.getConversionExecutor(sourceComponentType, targetComponentType); + } + } } 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 73672df0..140f7c63 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 @@ -32,7 +32,7 @@ import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.JdkVersion; /** - * Special one-way converter that converts from a source array to a target collection. Supports the selection of an + * Special converter that converts from a source array to a target collection. Supports the selection of an * "approximate" collection implementation when a target collection interface such as List.class is * specified. Supports type conversion of array elements when a concrete parameterized collection class is provided, * such as IntegerList.class. @@ -48,10 +48,16 @@ public class ArrayToCollection implements TwoWayConverter { private ConversionService conversionService; + private ConversionExecutor elementConverter; + public ArrayToCollection(ConversionService conversionService) { this.conversionService = conversionService; } + public ArrayToCollection(ConversionExecutor elementConverter) { + this.elementConverter = elementConverter; + } + public Class getSourceClass() { return Object[].class; } @@ -67,7 +73,7 @@ public class ArrayToCollection implements TwoWayConverter { Class collectionImplClass = getCollectionImplClass(targetClass); Constructor constructor = collectionImplClass.getConstructor(null); Collection collection = (Collection) constructor.newInstance(null); - ConversionExecutor converter = getElementConverter(source, targetClass); + ConversionExecutor converter = getArrayElementConverter(source, targetClass); int length = Array.getLength(source); for (int i = 0; i < length; i++) { Object value = Array.get(source, i); @@ -114,15 +120,19 @@ public class ArrayToCollection implements TwoWayConverter { } } - 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); + private ConversionExecutor getArrayElementConverter(Object source, Class targetClass) { + 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/DefaultConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java index 3dd48b78..04810f18 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.CollectionToCollection; import org.springframework.binding.convert.converters.NumberToNumber; import org.springframework.binding.convert.converters.ObjectToCollection; import org.springframework.binding.convert.converters.StringToBigDecimal; @@ -72,8 +73,9 @@ public class DefaultConversionService extends GenericConversionService { addConverter(new StringToLocale()); addConverter(new StringToDate()); addConverter(new StringToLabeledEnum()); - addConverter(new ObjectToCollection(this)); addConverter(new NumberToNumber()); + addConverter(new ObjectToCollection(this)); + addConverter(new CollectionToCollection(this)); if (ClassUtils.isPresent("java.lang.Enum", this.getClass().getClassLoader())) { addConverter(new StringToEnum()); } 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 106b0b9b..c8ab4e9a 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 @@ -130,6 +130,7 @@ public class GenericConversionService implements ConversionService { if (targetClass.isAssignableFrom(sourceClass)) { return new StaticConversionExecutor(sourceClass, targetClass, new NoOpConverter(sourceClass, targetClass)); } + // special handling for arrays since they are not indexable classes if (sourceClass.isArray()) { if (targetClass.isArray()) { return new StaticConversionExecutor(sourceClass, targetClass, new ArrayToArray(this)); @@ -183,6 +184,66 @@ public class GenericConversionService implements ConversionService { } sourceClass = convertToWrapperClassIfNecessary(sourceClass); targetClass = convertToWrapperClassIfNecessary(targetClass); + if (sourceClass.isArray()) { + Class sourceComponentType = sourceClass.getComponentType(); + if (targetClass.isArray()) { + Class targetComponentType = targetClass.getComponentType(); + 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 + "]"); + } + ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType, + targetComponentType, converter); + return new StaticConversionExecutor(sourceClass, targetClass, new ArrayToArray(elementConverter)); + } else if (converter.getTargetClass().isAssignableFrom(sourceComponentType) + && converter instanceof TwoWayConverter) { + TwoWayConverter twoWay = (TwoWayConverter) converter; + ConversionExecutor elementConverter = new StaticConversionExecutor(sourceComponentType, + targetComponentType, new ReverseConverter(twoWay)); + return new StaticConversionExecutor(sourceClass, targetClass, new ArrayToArray(elementConverter)); + } else { + throw new ConversionExecutorNotFoundException(sourceClass, targetClass, + "Custom ConversionExecutor with id '" + id + "' cannot convert from array of type [" + + sourceComponentType + "]; to an array of type [" + targetComponentType + "]"); + } + } 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 (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); + 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)); + 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 + "]"); + } + } + } + if (targetClass.isArray()) { + if (Collection.class.isAssignableFrom(sourceClass)) { + // type erasure limits us here as well + throw new UnsupportedOperationException("Not yet implemented"); + } else { + throw new UnsupportedOperationException("Not yet implemented"); + } + } if (converter.getSourceClass().isAssignableFrom(sourceClass)) { if (!converter.getTargetClass().isAssignableFrom(targetClass)) { throw new ConversionExecutorNotFoundException(sourceClass, 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 9d69bc6e..ff7a4b61 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 @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -168,6 +169,34 @@ public class DefaultConversionServiceTests extends TestCase { } } + public void testRegisterCustomConverterArrayToArray() { + DefaultConversionService service = new DefaultConversionService(); + service.addConverter("princy", new CustomTwoWayConverter()); + ConversionExecutor executor = service.getConversionExecutor("princy", String[].class, Principal[].class); + Principal[] p = (Principal[]) executor.execute(new String[] { "princy1", "princy2" }); + assertEquals("princy1", p[0].getName()); + assertEquals("princy2", p[1].getName()); + } + + public void testRegisterCustomConverterArrayToArrayReverse() { + 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"; + } + }; + final Principal princy2 = new Principal() { + public String getName() { + return "princy2"; + } + }; + String[] p = (String[]) executor.execute(new Principal[] { princy1, princy2 }); + assertEquals("princy1", p[0]); + assertEquals("princy2", p[1]); + } + public void testConversionPrimitive() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String.class, int.class); @@ -175,7 +204,7 @@ public class DefaultConversionServiceTests extends TestCase { assertEquals(3, three.intValue()); } - public void testArrayConversion() { + public void testArrayToArrayConversion() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String[].class, Integer[].class); Integer[] result = (Integer[]) executor.execute(new String[] { "1", "2", "3" }); @@ -184,7 +213,7 @@ public class DefaultConversionServiceTests extends TestCase { assertEquals(new Integer(3), result[2]); } - public void testPrimitiveArrayConversion() { + public void testArrayToArrayPrimitiveConversion() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String[].class, int[].class); int[] result = (int[]) executor.execute(new String[] { "1", "2", "3" }); @@ -193,7 +222,7 @@ public class DefaultConversionServiceTests extends TestCase { assertEquals(3, result[2]); } - public void testArrayListConversion() { + public void testArrayToListConversion() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String[].class, List.class); List result = (List) executor.execute(new String[] { "1", "2", "3" }); @@ -202,7 +231,7 @@ public class DefaultConversionServiceTests extends TestCase { assertEquals("3", result.get(2)); } - public void testListArrayConversion() { + public void testListToArrayConversion() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(Collection.class, String[].class); List list = new ArrayList(); @@ -215,7 +244,20 @@ public class DefaultConversionServiceTests extends TestCase { assertEquals("3", result[2]); } - public void testListArrayConversionWithComponentConversion() { + public void testSetToListConversion() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(Set.class, List.class); + Set set = new LinkedHashSet(); + set.add("1"); + set.add("2"); + set.add("3"); + List result = (List) executor.execute(set); + assertEquals("1", result.get(0)); + assertEquals("2", result.get(1)); + assertEquals("3", result.get(2)); + } + + public void testListToArrayConversionWithComponentConversion() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(Collection.class, Integer[].class); List list = new ArrayList(); @@ -228,7 +270,7 @@ public class DefaultConversionServiceTests extends TestCase { assertEquals(new Integer(3), result[2]); } - public void testArrayLinkedListConversion() { + public void testArrayToLinkedListConversion() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String[].class, LinkedList.class); LinkedList result = (LinkedList) executor.execute(new String[] { "1", "2", "3" }); @@ -246,7 +288,7 @@ public class DefaultConversionServiceTests extends TestCase { } } - public void testToArrayConversion() { + public void testStringToArrayConversion() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String.class, String[].class); String[] result = (String[]) executor.execute("1,2,3"); @@ -254,7 +296,7 @@ public class DefaultConversionServiceTests extends TestCase { assertEquals("1,2,3", result[0]); } - public void testToListConversion() { + public void testStringToListConversion() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String.class, List.class); List result = (List) executor.execute("1,2,3"); @@ -262,7 +304,7 @@ public class DefaultConversionServiceTests extends TestCase { assertEquals("1,2,3", result.get(0)); } - public void testToArrayConversionWithElementConversion() { + public void testStringToArrayConversionWithElementConversion() { DefaultConversionService service = new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String.class, Integer[].class); Integer[] result = (Integer[]) executor.execute("123");