Skip to content

Commit 5f8faa3

Browse files
author
Keith Donald
committed
improved null handling and javadoc
1 parent 0adcb2a commit 5f8faa3

File tree

5 files changed

+107
-15
lines changed

5 files changed

+107
-15
lines changed

org.springframework.beans/src/test/java/org/springframework/beans/BeanWrapperTests.java

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@
1616

1717
package org.springframework.beans;
1818

19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertFalse;
21+
import static org.junit.Assert.assertNotNull;
22+
import static org.junit.Assert.assertSame;
23+
import static org.junit.Assert.assertTrue;
24+
import static org.junit.Assert.fail;
25+
1926
import java.beans.PropertyEditorSupport;
2027
import java.math.BigDecimal;
2128
import java.math.BigInteger;
@@ -36,22 +43,23 @@
3643
import java.util.TreeSet;
3744

3845
import org.apache.commons.logging.LogFactory;
39-
import static org.junit.Assert.*;
46+
import org.junit.Ignore;
4047
import org.junit.Test;
41-
import test.beans.BooleanTestBean;
42-
import test.beans.ITestBean;
43-
import test.beans.IndexedTestBean;
44-
import test.beans.NumberTestBean;
45-
import test.beans.TestBean;
46-
4748
import org.springframework.beans.factory.annotation.Autowire;
4849
import org.springframework.beans.propertyeditors.CustomNumberEditor;
4950
import org.springframework.beans.propertyeditors.StringArrayPropertyEditor;
5051
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
5152
import org.springframework.beans.support.DerivedFromProtectedBaseBean;
53+
import org.springframework.core.convert.support.DefaultConversionService;
5254
import org.springframework.util.StopWatch;
5355
import org.springframework.util.StringUtils;
5456

57+
import test.beans.BooleanTestBean;
58+
import test.beans.ITestBean;
59+
import test.beans.IndexedTestBean;
60+
import test.beans.NumberTestBean;
61+
import test.beans.TestBean;
62+
5563
/**
5664
* @author Rod Johnson
5765
* @author Juergen Hoeller
@@ -61,6 +69,31 @@
6169
*/
6270
public final class BeanWrapperTests {
6371

72+
@Test
73+
@Ignore
74+
public void testNullNestedTypeDescriptor() {
75+
Foo foo = new Foo();
76+
BeanWrapperImpl wrapper = new BeanWrapperImpl(foo);
77+
wrapper.setConversionService(new DefaultConversionService());
78+
wrapper.setAutoGrowNestedPaths(true);
79+
wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9");
80+
assertEquals(9, foo.listOfMaps.get(0).get("luckyNumber"));
81+
}
82+
83+
static class Foo {
84+
85+
private List<Map> listOfMaps;
86+
87+
public List<Map> getListOfMaps() {
88+
return listOfMaps;
89+
}
90+
91+
public void setListOfMaps(List<Map> listOfMaps) {
92+
this.listOfMaps = listOfMaps;
93+
}
94+
95+
}
96+
6497
@Test
6598
public void testIsReadablePropertyNotReadable() {
6699
NoRead nr = new NoRead();

org.springframework.core/src/main/java/org/springframework/core/convert/ConversionService.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public interface ConversionService {
3030
* @param sourceType the source type to convert from (required)
3131
* @param targetType the target type to convert to (required)
3232
* @return true if a conversion can be performed, false if not
33+
* @throws IllegalArgumentException if targetType is null
3334
*/
3435
boolean canConvert(Class<?> sourceType, Class<?> targetType);
3536

@@ -39,6 +40,7 @@ public interface ConversionService {
3940
* @param targetType the target type to convert to (required)
4041
* @return the converted object, an instance of targetType
4142
* @throws ConversionException if an exception occurred
43+
* @throws IllegalArgumentException if targetType is null
4244
*/
4345
<T> T convert(Object source, Class<T> targetType);
4446

@@ -49,6 +51,8 @@ public interface ConversionService {
4951
* @param sourceType context about the source type to convert from (required)
5052
* @param targetType context about the target type to convert to (required)
5153
* @return true if a conversion can be performed between the source and target types, false if not
54+
* @throws IllegalArgumentException if targetType is null
55+
* @see TypeDescriptor#forObject(Object)
5256
*/
5357
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
5458

@@ -57,10 +61,13 @@ public interface ConversionService {
5761
* The TypeDescriptors provide additional context about the field locations where conversion will occur, often object property locations.
5862
* This flavor of the convert operation exists mainly for use by a general purpose data mapping framework, and not for use by user code.
5963
* @param source the source object to convert (may be null)
60-
* @param sourceType context about the source type converting from (required)
64+
* @param sourceType context about the source type converting from (may be null if source is null)
6165
* @param targetType context about the target type to convert to (required)
6266
* @return the converted object, an instance of {@link TypeDescriptor#getObjectType() targetType}</code>
6367
* @throws ConversionException if an exception occurred
68+
* @throws IllegalArgumentException if targetType is null
69+
* @throws IllegalArgumentException if sourceType is null but source is not null
70+
* @see TypeDescriptor#forObject(Object)
6471
*/
6572
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
6673

org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,12 @@ public static TypeDescriptor map(Class<?> mapType, TypeDescriptor keyType, TypeD
144144
* If the methodParameter is a List<List<String>> and the nestingLevel is 2, the nested type descriptor will also be a String.class.
145145
* If the methodParameter is a Map<Integer, String> and the nesting level is 1, the nested type descriptor will be String, derived from the map value.
146146
* If the methodParameter is a List<Map<Integer, String>> and the nesting level is 2, the nested type descriptor will be String, derived from the map value.
147+
* Returns null if a nested type cannot be obtained because it was not declared.
148+
* For example, if the method parameter is a List&lt;?&gt;, the nested type descriptor returned will be null.
147149
* @param methodParameter the method parameter with a nestingLevel of 1
148150
* @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the method parameter.
149-
* @return the nested type descriptor
150-
* @throws IllegalArgumentException if the method parameter is not of a collection, array, or map type.
151+
* @return the nested type descriptor at the specified nesting level, or null if it could not be obtained.
152+
* @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types.
151153
*/
152154
public static TypeDescriptor nested(MethodParameter methodParameter, int nestingLevel) {
153155
return nested(new ParameterDescriptor(methodParameter), nestingLevel);
@@ -159,10 +161,12 @@ public static TypeDescriptor nested(MethodParameter methodParameter, int nesting
159161
* If the field is a List<List<String>> and the nestingLevel is 2, the nested type descriptor will also be a String.class.
160162
* If the field is a Map<Integer, String> and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value.
161163
* If the field is a List<Map<Integer, String>> and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value.
164+
* Returns null if a nested type cannot be obtained because it was not declared.
165+
* For example, if the field is a List&lt;?&gt;, the nested type descriptor returned will be null.
162166
* @param field the field
163167
* @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the field.
164-
* @return the nested type descriptor
165-
* @throws IllegalArgumentException if the field is not of a collection, array, or map type.
168+
* @return the nested type descriptor at the specified nestingLevel, or null if it could not be obtained
169+
* @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types.
166170
*/
167171
public static TypeDescriptor nested(Field field, int nestingLevel) {
168172
return nested(new FieldDescriptor(field), nestingLevel);
@@ -174,10 +178,12 @@ public static TypeDescriptor nested(Field field, int nestingLevel) {
174178
* If the property is a List<List<String>> and the nestingLevel is 2, the nested type descriptor will also be a String.class.
175179
* If the field is a Map<Integer, String> and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value.
176180
* If the property is a List<Map<Integer, String>> and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value.
181+
* Returns null if a nested type cannot be obtained because it was not declared.
182+
* For example, if the property is a List&lt;?&gt;, the nested type descriptor returned will be null.
177183
* @param property the property
178184
* @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the property.
179-
* @return the nested type descriptor
180-
* @throws IllegalArgumentException if the property is not of a collection, array, or map type.
185+
* @return the nested type descriptor at the specified nestingLevel, or null if it could not be obtained
186+
* @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types.
181187
*/
182188
public static TypeDescriptor nested(Property property, int nestingLevel) {
183189
return nested(new BeanPropertyDescriptor(property), nestingLevel);

org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
133133
// implementing ConversionService
134134

135135
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
136-
return canConvert(TypeDescriptor.valueOf(sourceType), TypeDescriptor.valueOf(targetType));
136+
if (targetType == null) {
137+
throw new IllegalArgumentException("The targetType to convert to cannot be null");
138+
}
139+
return canConvert(sourceType != null ? TypeDescriptor.valueOf(sourceType) : null, TypeDescriptor.valueOf(targetType));
137140
}
138141

139142
@SuppressWarnings("unchecked")
@@ -145,6 +148,12 @@ public <T> T convert(Object source, Class<T> targetType) {
145148
}
146149

147150
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
151+
if (targetType == null) {
152+
throw new IllegalArgumentException("The targetType to convert to cannot be null");
153+
}
154+
if (sourceType == null) {
155+
return true;
156+
}
148157
if (logger.isTraceEnabled()) {
149158
logger.trace("Checking if I can convert " + sourceType + " to " + targetType);
150159
}

org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,43 @@ public class GenericConversionServiceTests {
5050

5151
private GenericConversionService conversionService = new GenericConversionService();
5252

53+
@Test
54+
public void canConvert() {
55+
assertFalse(conversionService.canConvert(String.class, Integer.class));
56+
conversionService.addConverterFactory(new StringToNumberConverterFactory());
57+
assertTrue(conversionService.canConvert(String.class, Integer.class));
58+
}
59+
60+
@Test
61+
public void canConvertAssignable() {
62+
assertTrue(conversionService.canConvert(String.class, String.class));
63+
assertTrue(conversionService.canConvert(Integer.class, Number.class));
64+
assertTrue(conversionService.canConvert(boolean.class, boolean.class));
65+
assertTrue(conversionService.canConvert(boolean.class, Boolean.class));
66+
}
67+
68+
@Test
69+
public void canConvertIllegalArgumentNullTargetType() {
70+
try {
71+
assertFalse(conversionService.canConvert(String.class, null));
72+
fail("Should have failed");
73+
} catch (IllegalArgumentException e) {
74+
75+
}
76+
try {
77+
assertFalse(conversionService.canConvert(TypeDescriptor.valueOf(String.class), null));
78+
fail("Should have failed");
79+
} catch (IllegalArgumentException e) {
80+
81+
}
82+
}
83+
84+
@Test
85+
public void canConvertNullSourceType() {
86+
assertTrue(conversionService.canConvert(null, Integer.class));
87+
assertTrue(conversionService.canConvert(null, TypeDescriptor.valueOf(Integer.class)));
88+
}
89+
5390
@Test
5491
public void convert() {
5592
conversionService.addConverterFactory(new StringToNumberConverterFactory());

0 commit comments

Comments
 (0)