提交 5f8faa3a 编写于 作者: K Keith Donald

improved null handling and javadoc

上级 0adcb2ad
......@@ -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<Map> listOfMaps;
public List<Map> getListOfMaps() {
return listOfMaps;
}
public void setListOfMaps(List<Map> listOfMaps) {
this.listOfMaps = listOfMaps;
}
}
@Test
public void testIsReadablePropertyNotReadable() {
NoRead nr = new NoRead();
......
......@@ -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> T convert(Object source, Class<T> 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}</code>
* @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);
......
......@@ -144,10 +144,12 @@ public class TypeDescriptor {
* If the methodParameter is a List<List<String>> and the nestingLevel is 2, the nested type descriptor will also be a String.class.
* 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.
* 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.
* Returns null if a nested type cannot be obtained because it was not declared.
* For example, if the method parameter is a List&lt;?&gt;, 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<List<String>> and the nestingLevel is 2, the nested type descriptor will also be a String.class.
* 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.
* 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.
* Returns null if a nested type cannot be obtained because it was not declared.
* For example, if the field is a List&lt;?&gt;, 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<List<String>> and the nestingLevel is 2, the nested type descriptor will also be a String.class.
* 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.
* 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.
* Returns null if a nested type cannot be obtained because it was not declared.
* For example, if the property is a List&lt;?&gt;, 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);
......
......@@ -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);
}
......
......@@ -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());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册