From eaa8a4271c56a677d6cd8a801136d0734e16fcd5 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Sun, 5 Jun 2011 17:41:08 +0000 Subject: [PATCH] improved null handling and javadoc git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4458 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../beans/BeanWrapperTests.java | 47 ++++++++++++++++--- .../core/convert/ConversionService.java | 9 +++- .../core/convert/TypeDescriptor.java | 18 ++++--- .../support/GenericConversionService.java | 11 ++++- .../GenericConversionServiceTests.java | 37 +++++++++++++++ 5 files changed, 107 insertions(+), 15 deletions(-) diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/BeanWrapperTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/BeanWrapperTests.java index ee9b59e510a..69fbafdf7d3 100644 --- a/org.springframework.beans/src/test/java/org/springframework/beans/BeanWrapperTests.java +++ b/org.springframework.beans/src/test/java/org/springframework/beans/BeanWrapperTests.java @@ -16,6 +16,13 @@ package org.springframework.beans; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.beans.PropertyEditorSupport; import java.math.BigDecimal; import java.math.BigInteger; @@ -36,22 +43,23 @@ import java.util.TreeMap; import java.util.TreeSet; import org.apache.commons.logging.LogFactory; -import static org.junit.Assert.*; +import org.junit.Ignore; import org.junit.Test; -import test.beans.BooleanTestBean; -import test.beans.ITestBean; -import test.beans.IndexedTestBean; -import test.beans.NumberTestBean; -import test.beans.TestBean; - import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.propertyeditors.CustomNumberEditor; import org.springframework.beans.propertyeditors.StringArrayPropertyEditor; import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.beans.support.DerivedFromProtectedBaseBean; +import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.util.StopWatch; import org.springframework.util.StringUtils; +import test.beans.BooleanTestBean; +import test.beans.ITestBean; +import test.beans.IndexedTestBean; +import test.beans.NumberTestBean; +import test.beans.TestBean; + /** * @author Rod Johnson * @author Juergen Hoeller @@ -61,6 +69,31 @@ import org.springframework.util.StringUtils; */ public final class BeanWrapperTests { + @Test + @Ignore + public void testNullNestedTypeDescriptor() { + Foo foo = new Foo(); + BeanWrapperImpl wrapper = new BeanWrapperImpl(foo); + wrapper.setConversionService(new DefaultConversionService()); + wrapper.setAutoGrowNestedPaths(true); + wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9"); + assertEquals(9, foo.listOfMaps.get(0).get("luckyNumber")); + } + + static class Foo { + + private List listOfMaps; + + public List getListOfMaps() { + return listOfMaps; + } + + public void setListOfMaps(List listOfMaps) { + this.listOfMaps = listOfMaps; + } + + } + @Test public void testIsReadablePropertyNotReadable() { NoRead nr = new NoRead(); diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java index 97bce8933da..58166818fdd 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java @@ -30,6 +30,7 @@ public interface ConversionService { * @param sourceType the source type to convert from (required) * @param targetType the target type to convert to (required) * @return true if a conversion can be performed, false if not + * @throws IllegalArgumentException if targetType is null */ boolean canConvert(Class sourceType, Class targetType); @@ -39,6 +40,7 @@ public interface ConversionService { * @param targetType the target type to convert to (required) * @return the converted object, an instance of targetType * @throws ConversionException if an exception occurred + * @throws IllegalArgumentException if targetType is null */ T convert(Object source, Class targetType); @@ -49,6 +51,8 @@ public interface ConversionService { * @param sourceType context about the source type to convert from (required) * @param targetType context about the target type to convert to (required) * @return true if a conversion can be performed between the source and target types, false if not + * @throws IllegalArgumentException if targetType is null + * @see TypeDescriptor#forObject(Object) */ boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); @@ -57,10 +61,13 @@ public interface ConversionService { * The TypeDescriptors provide additional context about the field locations where conversion will occur, often object property locations. * This flavor of the convert operation exists mainly for use by a general purpose data mapping framework, and not for use by user code. * @param source the source object to convert (may be null) - * @param sourceType context about the source type converting from (required) + * @param sourceType context about the source type converting from (may be null if source is null) * @param targetType context about the target type to convert to (required) * @return the converted object, an instance of {@link TypeDescriptor#getObjectType() targetType} * @throws ConversionException if an exception occurred + * @throws IllegalArgumentException if targetType is null + * @throws IllegalArgumentException if sourceType is null but source is not null + * @see TypeDescriptor#forObject(Object) */ Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); 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 37e8bb92a9e..bc609aa75dc 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 @@ -144,10 +144,12 @@ public class TypeDescriptor { * If the methodParameter is a List> and the nestingLevel is 2, the nested type descriptor will also be a String.class. * If the methodParameter is a Map and the nesting level is 1, the nested type descriptor will be String, derived from the map value. * If the methodParameter is a List> and the nesting level is 2, the nested type descriptor will be String, derived from the map value. + * Returns null if a nested type cannot be obtained because it was not declared. + * For example, if the method parameter is a List<?>, the nested type descriptor returned will be null. * @param methodParameter the method parameter with a nestingLevel of 1 * @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the method parameter. - * @return the nested type descriptor - * @throws IllegalArgumentException if the method parameter is not of a collection, array, or map type. + * @return the nested type descriptor at the specified nesting level, or null if it could not be obtained. + * @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types. */ public static TypeDescriptor nested(MethodParameter methodParameter, int nestingLevel) { return nested(new ParameterDescriptor(methodParameter), nestingLevel); @@ -159,10 +161,12 @@ public class TypeDescriptor { * If the field is a List> and the nestingLevel is 2, the nested type descriptor will also be a String.class. * If the field is a Map and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value. * If the field is a List> and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value. + * Returns null if a nested type cannot be obtained because it was not declared. + * For example, if the field is a List<?>, the nested type descriptor returned will be null. * @param field the field * @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the field. - * @return the nested type descriptor - * @throws IllegalArgumentException if the field is not of a collection, array, or map type. + * @return the nested type descriptor at the specified nestingLevel, or null if it could not be obtained + * @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types. */ public static TypeDescriptor nested(Field field, int nestingLevel) { return nested(new FieldDescriptor(field), nestingLevel); @@ -174,10 +178,12 @@ public class TypeDescriptor { * If the property is a List> and the nestingLevel is 2, the nested type descriptor will also be a String.class. * If the field is a Map and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value. * If the property is a List> and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value. + * Returns null if a nested type cannot be obtained because it was not declared. + * For example, if the property is a List<?>, the nested type descriptor returned will be null. * @param property the property * @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the property. - * @return the nested type descriptor - * @throws IllegalArgumentException if the property is not of a collection, array, or map type. + * @return the nested type descriptor at the specified nestingLevel, or null if it could not be obtained + * @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types. */ public static TypeDescriptor nested(Property property, int nestingLevel) { return nested(new BeanPropertyDescriptor(property), nestingLevel); diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index 5752247ccb3..d8322c5405f 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -133,7 +133,10 @@ public class GenericConversionService implements ConfigurableConversionService { // implementing ConversionService public boolean canConvert(Class sourceType, Class targetType) { - return canConvert(TypeDescriptor.valueOf(sourceType), TypeDescriptor.valueOf(targetType)); + if (targetType == null) { + throw new IllegalArgumentException("The targetType to convert to cannot be null"); + } + return canConvert(sourceType != null ? TypeDescriptor.valueOf(sourceType) : null, TypeDescriptor.valueOf(targetType)); } @SuppressWarnings("unchecked") @@ -145,6 +148,12 @@ public class GenericConversionService implements ConfigurableConversionService { } public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) { + if (targetType == null) { + throw new IllegalArgumentException("The targetType to convert to cannot be null"); + } + if (sourceType == null) { + return true; + } if (logger.isTraceEnabled()) { logger.trace("Checking if I can convert " + sourceType + " to " + targetType); } diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java index be679632121..4eb186f93fe 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java @@ -50,6 +50,43 @@ public class GenericConversionServiceTests { private GenericConversionService conversionService = new GenericConversionService(); + @Test + public void canConvert() { + assertFalse(conversionService.canConvert(String.class, Integer.class)); + conversionService.addConverterFactory(new StringToNumberConverterFactory()); + assertTrue(conversionService.canConvert(String.class, Integer.class)); + } + + @Test + public void canConvertAssignable() { + assertTrue(conversionService.canConvert(String.class, String.class)); + assertTrue(conversionService.canConvert(Integer.class, Number.class)); + assertTrue(conversionService.canConvert(boolean.class, boolean.class)); + assertTrue(conversionService.canConvert(boolean.class, Boolean.class)); + } + + @Test + public void canConvertIllegalArgumentNullTargetType() { + try { + assertFalse(conversionService.canConvert(String.class, null)); + fail("Should have failed"); + } catch (IllegalArgumentException e) { + + } + try { + assertFalse(conversionService.canConvert(TypeDescriptor.valueOf(String.class), null)); + fail("Should have failed"); + } catch (IllegalArgumentException e) { + + } + } + + @Test + public void canConvertNullSourceType() { + assertTrue(conversionService.canConvert(null, Integer.class)); + assertTrue(conversionService.canConvert(null, TypeDescriptor.valueOf(Integer.class))); + } + @Test public void convert() { conversionService.addConverterFactory(new StringToNumberConverterFactory());