added null handling, fixed bug relating to conversion of non-generic collections

This commit is contained in:
Keith Donald 2009-10-18 22:20:53 +00:00
parent 206963bcba
commit 0e4064fecc
19 changed files with 115 additions and 124 deletions

View File

@ -81,6 +81,9 @@ public class FormattingConversionServiceAdapter extends GenericConversionService
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
try {
return this.formatter.parse((String) source, LocaleContextHolder.getLocale());
}

View File

@ -90,7 +90,11 @@ public class TypeDescriptor {
private TypeDescriptor() {
}
private TypeDescriptor(Class collectionType, TypeDescriptor elementType) {
/**
* Internal constructor for a Collection TypeDescriptor.
* @see #collection(Class, TypeDescriptor)
*/
private TypeDescriptor(Class<?> collectionType, TypeDescriptor elementType) {
this.type = collectionType;
this.elementType = elementType;
}
@ -381,6 +385,16 @@ public class TypeDescriptor {
return (object == null ? NULL : valueOf(object.getClass()));
}
/**
* Create a TypeDescriptor for a Collection type.
* @param type the collection type
* @param elementType the collection's element type
* @return the type descriptor
*/
public static TypeDescriptor collection(Class<?> type, TypeDescriptor elementType) {
return new TypeDescriptor(type, elementType);
}
public String toString() {
if (this == TypeDescriptor.NULL) {
return "[TypeDescriptor.NULL]";
@ -389,8 +403,4 @@ public class TypeDescriptor {
}
}
public static TypeDescriptor collection(Class<?> type, TypeDescriptor elementType) {
return new TypeDescriptor(type, elementType);
}
}

View File

@ -34,7 +34,7 @@ final class ArrayToArrayConverter implements GenericConverter {
this.helperConverter = new CollectionToArrayConverter(conversionService);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.convert(asList(source), sourceType, targetType);
}

View File

@ -40,6 +40,9 @@ final class ArrayToCollectionConverter implements GenericConverter {
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
int length = Array.getLength(source);
Collection collection = CollectionFactory.createCollection(targetType.getType(), length);
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();

View File

@ -41,6 +41,9 @@ final class CollectionToArrayConverter implements GenericConverter {
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
Collection sourceCollection = (Collection) source;
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL) {

View File

@ -16,12 +16,14 @@
package org.springframework.core.convert.support;
import static org.springframework.core.convert.support.ConversionUtils.getElementType;
import static org.springframework.core.convert.support.ConversionUtils.invokeConverter;
import java.util.Collection;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
import static org.springframework.core.convert.support.ConversionUtils.*;
/**
* Converts from a source Collection to target Collection type.
@ -39,17 +41,20 @@ final class CollectionToCollectionConverter implements GenericConverter {
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
Collection sourceCollection = (Collection) source;
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL) {
sourceElementType = getElementType(sourceCollection);
}
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL || sourceElementType.isAssignableTo(targetElementType)) {
if (sourceElementType == TypeDescriptor.NULL || targetElementType == TypeDescriptor.NULL
|| sourceElementType.isAssignableTo(targetElementType)) {
if (sourceType.isAssignableTo(targetType)) {
return sourceCollection;
}
else {
} else {
Collection target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size());
target.addAll(sourceCollection);
return target;

View File

@ -39,6 +39,9 @@ final class CollectionToMapConverter implements GenericConverter {
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
Collection sourceCollection = (Collection) source;
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL) {

View File

@ -41,16 +41,17 @@ final class CollectionToObjectConverter implements GenericConverter {
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
Collection sourceCollection = (Collection) source;
if (sourceCollection.size() == 0) {
if (targetType.typeEquals(String.class)) {
return "";
}
else {
} else {
return null;
}
}
else {
} else {
if (targetType.typeEquals(String.class)) {
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL) {
@ -67,8 +68,7 @@ final class CollectionToObjectConverter implements GenericConverter {
i++;
}
return string.toString();
}
else {
} else {
GenericConverter converter = this.conversionService.getConverter(sourceElementType, targetType);
if (converter == null) {
throw new ConverterNotFoundException(sourceElementType, targetType);
@ -85,8 +85,7 @@ final class CollectionToObjectConverter implements GenericConverter {
}
return string.toString();
}
}
else {
} else {
Object firstElement = sourceCollection.iterator().next();
TypeDescriptor sourceElementType = sourceType.getElementTypeDescriptor();
if (sourceElementType == TypeDescriptor.NULL && firstElement != null) {
@ -94,8 +93,7 @@ final class CollectionToObjectConverter implements GenericConverter {
}
if (sourceElementType == TypeDescriptor.NULL || sourceElementType.isAssignableTo(targetType)) {
return firstElement;
}
else {
} else {
GenericConverter converter = this.conversionService.getConverter(sourceElementType, targetType);
if (converter == null) {
throw new ConverterNotFoundException(sourceElementType, targetType);

View File

@ -46,7 +46,7 @@ final class ConversionUtils {
public static List asList(Object array) {
return new ArrayList(array);
return array != null ? new ArrayList(array) : null;
}
private static class ArrayList extends AbstractList implements RandomAccess, java.io.Serializable {

View File

@ -1,25 +0,0 @@
/**
*
*/
package org.springframework.core.convert.support;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConverterFactory;
/**
* Adapts a ConverterFactory to the uniform GenericConverter interface.
* @author Keith Donald
*/
@SuppressWarnings("unchecked")
public final class ConverterFactoryGenericConverter implements GenericConverter {
private final ConverterFactory converterFactory;
public ConverterFactoryGenericConverter(ConverterFactory converterFactory) {
this.converterFactory = converterFactory;
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
}
}

View File

@ -1,37 +0,0 @@
/*
* Copyright 2002-2009 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.core.convert.support;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
/**
* Adapts a Converter to the uniform GenericConverter interface.
* @author Keith Donald
*/
@SuppressWarnings("unchecked")
public final class ConverterGenericConverter implements GenericConverter {
private final Converter converter;
public ConverterGenericConverter(Converter converter) {
this.converter = converter;
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.converter.convert(source);
}
}

View File

@ -16,9 +16,10 @@
package org.springframework.core.convert.support;
import static org.springframework.core.convert.support.ConversionUtils.invokeConverter;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
import static org.springframework.core.convert.support.ConversionUtils.*;
/**
* Helper for converting map entries.
@ -35,21 +36,19 @@ class MapEntryConverter {
private TypeDescriptor sourceKeyType;
private TypeDescriptor sourceValueType;
private TypeDescriptor targetKeyType;
private TypeDescriptor targetValueType;
public MapEntryConverter(TypeDescriptor sourceKeyType, TypeDescriptor sourceValueType, TypeDescriptor targetKeyType,
TypeDescriptor targetValueType, boolean keysCompatible, boolean valuesCompatible,
GenericConversionService conversionService) {
public MapEntryConverter(TypeDescriptor sourceKeyType, TypeDescriptor sourceValueType,
TypeDescriptor targetKeyType, TypeDescriptor targetValueType, boolean keysCompatible,
boolean valuesCompatible, GenericConversionService conversionService) {
if (sourceKeyType != TypeDescriptor.NULL && targetKeyType != TypeDescriptor.NULL && !keysCompatible) {
this.keyConverter = conversionService.getConverter(sourceKeyType, targetKeyType);
if (this.keyConverter == null) {
throw new ConverterNotFoundException(sourceKeyType, targetKeyType);
}
}
this.sourceKeyType = sourceKeyType;
this.targetKeyType = targetKeyType;
}
@ -57,18 +56,16 @@ class MapEntryConverter {
this.valueConverter = conversionService.getConverter(sourceValueType, targetValueType);
if (this.valueConverter == null) {
throw new ConverterNotFoundException(sourceValueType, targetValueType);
}
}
this.sourceValueType = sourceValueType;
this.targetValueType = targetValueType;
}
}
public Object convertKey(Object sourceKey) {
if (sourceKey != null && this.keyConverter != null) {
return invokeConverter(this.keyConverter, sourceKey, this.sourceKeyType, this.targetKeyType);
}
else {
} else {
return sourceKey;
}
}
@ -76,8 +73,7 @@ class MapEntryConverter {
public Object convertValue(Object sourceValue) {
if (sourceValue != null && this.valueConverter != null) {
return invokeConverter(this.valueConverter, sourceValue, this.sourceValueType, this.targetValueType);
}
else {
} else {
return sourceValue;
}
}

View File

@ -38,6 +38,9 @@ final class MapToCollectionConverter implements GenericConverter {
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
Map sourceMap = (Map) source;
TypeDescriptor sourceKeyType = sourceType.getMapKeyTypeDescriptor();
TypeDescriptor sourceValueType = sourceType.getMapValueTypeDescriptor();
@ -52,7 +55,7 @@ final class MapToCollectionConverter implements GenericConverter {
}
Collection target = CollectionFactory.createCollection(targetType.getType(), sourceMap.size());
MapEntryConverter converter = new MapEntryConverter(sourceKeyType, sourceValueType, targetElementType,
targetElementType, keysCompatible, valuesCompatible, conversionService);
targetElementType, keysCompatible, valuesCompatible, this.conversionService);
if (targetElementType.getType().equals(String.class)) {
for (Object entry : sourceMap.entrySet()) {
Map.Entry mapEntry = (Map.Entry) entry;

View File

@ -37,6 +37,9 @@ final class MapToMapConverter implements GenericConverter {
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
Map sourceMap = (Map) source;
TypeDescriptor targetKeyType = targetType.getMapKeyTypeDescriptor();
TypeDescriptor targetValueType = targetType.getMapValueTypeDescriptor();
@ -98,8 +101,7 @@ final class MapToMapConverter implements GenericConverter {
private Map compatibleMapWithoutEntryConversion(Map source, TypeDescriptor targetType) {
if (targetType.getType().isAssignableFrom(source.getClass())) {
return source;
}
else {
} else {
Map target = CollectionFactory.createMap(targetType.getType(), source.size());
target.putAll(source);
return target;

View File

@ -37,16 +37,17 @@ final class MapToObjectConverter implements GenericConverter {
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
Map sourceMap = (Map) source;
if (sourceMap.size() == 0) {
if (targetType.typeEquals(String.class)) {
return "";
}
else {
} else {
return null;
}
}
else {
} else {
if (targetType.typeEquals(String.class)) {
TypeDescriptor sourceKeyType = sourceType.getMapKeyTypeDescriptor();
TypeDescriptor sourceValueType = sourceType.getMapValueTypeDescriptor();
@ -71,10 +72,9 @@ final class MapToObjectConverter implements GenericConverter {
i++;
}
return string.toString();
}
else {
MapEntryConverter converter = new MapEntryConverter(sourceKeyType, sourceValueType, targetType, targetType,
keysCompatible, valuesCompatible, conversionService);
} else {
MapEntryConverter converter = new MapEntryConverter(sourceKeyType, sourceValueType, targetType,
targetType, keysCompatible, valuesCompatible, this.conversionService);
StringBuilder string = new StringBuilder();
int i = 0;
for (Object entry : sourceMap.entrySet()) {
@ -90,8 +90,7 @@ final class MapToObjectConverter implements GenericConverter {
}
return string.toString();
}
}
else {
} else {
TypeDescriptor sourceValueType = sourceType.getMapValueTypeDescriptor();
boolean valuesCompatible = false;
if (sourceValueType == TypeDescriptor.NULL || sourceValueType.isAssignableTo(targetType)) {
@ -99,10 +98,9 @@ final class MapToObjectConverter implements GenericConverter {
}
if (valuesCompatible) {
return sourceMap.values().iterator().next();
}
else {
MapEntryConverter converter = new MapEntryConverter(sourceValueType, sourceValueType, targetType, targetType,
true, valuesCompatible, conversionService);
} else {
MapEntryConverter converter = new MapEntryConverter(sourceValueType, sourceValueType, targetType,
targetType, true, valuesCompatible, this.conversionService);
Object value = sourceMap.values().iterator().next();
return converter.convertValue(value);
}

View File

@ -39,6 +39,9 @@ final class ObjectToArrayConverter implements GenericConverter {
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
if (sourceType.typeEquals(String.class)) {
String string = (String) source;

View File

@ -39,7 +39,10 @@ final class ObjectToCollectionConverter implements GenericConverter {
this.conversionService = conversionService;
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
if (sourceType.typeEquals(String.class)) {
String string = (String) source;

View File

@ -30,7 +30,7 @@ import org.springframework.core.convert.TypeDescriptor;
final class ObjectToMapConverter implements GenericConverter {
private final GenericConversionService conversionService;
private final ArrayToMapConverter helperConverter;
public ObjectToMapConverter(GenericConversionService conversionService) {
@ -40,12 +40,14 @@ final class ObjectToMapConverter implements GenericConverter {
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return this.conversionService.convertNullSource(sourceType, targetType);
}
if (sourceType.typeEquals(String.class)) {
String string = (String) source;
String[] properties = string.split(" ");
return this.helperConverter.convert(properties, TypeDescriptor.valueOf(String[].class), targetType);
}
else {
} else {
Map target = CollectionFactory.createMap(targetType.getType(), 1);
TypeDescriptor targetKeyType = targetType.getMapKeyTypeDescriptor();
TypeDescriptor targetValueType = targetType.getMapValueTypeDescriptor();
@ -59,10 +61,9 @@ final class ObjectToMapConverter implements GenericConverter {
}
if (keysCompatible && valuesCompatible) {
target.put(source, source);
}
else {
} else {
MapEntryConverter converter = new MapEntryConverter(sourceType, sourceType, targetKeyType,
targetValueType, keysCompatible, valuesCompatible, conversionService);
targetValueType, keysCompatible, valuesCompatible, this.conversionService);
Object key = converter.convertKey(source);
Object value = converter.convertValue(source);
target.put(key, value);

View File

@ -131,7 +131,8 @@ public class GenericConversionServiceTests {
@Test
public void convertArrayToArray() {
conversionService.addGenericConverter(Object[].class, Object[].class, new ArrayToArrayConverter(conversionService));
conversionService.addGenericConverter(Object[].class, Object[].class, new ArrayToArrayConverter(
conversionService));
conversionService.addConverterFactory(new StringToNumberConverterFactory());
Integer[] result = conversionService.convert(new String[] { "1", "2", "3" }, Integer[].class);
assertEquals(new Integer(1), result[0]);
@ -255,12 +256,33 @@ public class GenericConversionServiceTests {
foo.add("1");
foo.add("2");
foo.add("3");
List<Integer> bar = (List<Integer>) conversionService.convert(foo, TypeDescriptor.valueOf(List.class),
List<Integer> bar = (List<Integer>) conversionService.convert(foo, TypeDescriptor.valueOf(LinkedHashSet.class),
new TypeDescriptor(getClass().getField("genericList")));
assertEquals(new Integer(1), bar.get(0));
assertEquals(new Integer(2), bar.get(1));
assertEquals(new Integer(3), bar.get(2));
}
@Test
public void convertCollectionToCollectionNull() throws Exception {
List<Integer> bar = (List<Integer>) conversionService.convert(null, TypeDescriptor.valueOf(LinkedHashSet.class),
new TypeDescriptor(getClass().getField("genericList")));
assertNull(bar);
}
@Test
public void convertCollectionToCollectionNotGeneric() throws Exception {
conversionService.addConverterFactory(new StringToNumberConverterFactory());
Set<String> foo = new LinkedHashSet<String>();
foo.add("1");
foo.add("2");
foo.add("3");
List bar = (List) conversionService.convert(foo, TypeDescriptor.valueOf(LinkedHashSet.class), TypeDescriptor
.valueOf(List.class));
assertEquals("1", bar.get(0));
assertEquals("2", bar.get(1));
assertEquals("3", bar.get(2));
}
@Test
public void convertCollectionToString() {