SPR-6350
This commit is contained in:
@@ -16,8 +16,9 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import static org.springframework.core.convert.support.ConversionUtils.asList;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import static org.springframework.core.convert.support.ConversionUtils.*;
|
||||
|
||||
/**
|
||||
* Converts from an array to a Map.
|
||||
@@ -27,7 +28,7 @@ import static org.springframework.core.convert.support.ConversionUtils.*;
|
||||
*/
|
||||
final class ArrayToMapConverter implements GenericConverter {
|
||||
|
||||
private final GenericConverter helperConverter;
|
||||
private final CollectionToMapConverter helperConverter;
|
||||
|
||||
public ArrayToMapConverter(GenericConversionService conversionService) {
|
||||
this.helperConverter = new CollectionToMapConverter(conversionService);
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import static org.springframework.core.convert.support.ConversionUtils.getElementType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import static org.springframework.core.convert.support.ConversionUtils.*;
|
||||
import org.springframework.core.style.StylerUtils;
|
||||
|
||||
/**
|
||||
* Converts from a Collection to a Map.
|
||||
@@ -50,13 +52,11 @@ final class CollectionToMapConverter implements GenericConverter {
|
||||
TypeDescriptor targetKeyType = targetType.getMapKeyTypeDescriptor();
|
||||
TypeDescriptor targetValueType = targetType.getMapValueTypeDescriptor();
|
||||
boolean keysCompatible = false;
|
||||
if (sourceElementType == TypeDescriptor.NULL || targetKeyType == TypeDescriptor.NULL
|
||||
|| sourceElementType.isAssignableTo(targetKeyType)) {
|
||||
if (sourceElementType != TypeDescriptor.NULL && sourceElementType.isAssignableTo(targetKeyType)) {
|
||||
keysCompatible = true;
|
||||
}
|
||||
boolean valuesCompatible = false;
|
||||
if (sourceElementType == TypeDescriptor.NULL || targetValueType == TypeDescriptor.NULL
|
||||
|| sourceElementType.isAssignableTo(targetValueType)) {
|
||||
if (sourceElementType != TypeDescriptor.NULL && sourceElementType.isAssignableTo(targetValueType)) {
|
||||
valuesCompatible = true;
|
||||
}
|
||||
if (keysCompatible && valuesCompatible) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.lang.reflect.Array;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.RandomAccess;
|
||||
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
@@ -26,6 +27,9 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||
|
||||
final class ConversionUtils {
|
||||
|
||||
private ConversionUtils() {
|
||||
}
|
||||
|
||||
public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType,
|
||||
TypeDescriptor targetType) {
|
||||
try {
|
||||
@@ -44,6 +48,25 @@ final class ConversionUtils {
|
||||
return TypeDescriptor.NULL;
|
||||
}
|
||||
|
||||
public static TypeDescriptor[] getMapEntryTypes(Map<?, ?> sourceMap) {
|
||||
Class<?> keyType = null;
|
||||
Class<?> valueType = null;
|
||||
for (Object entry : sourceMap.entrySet()) {
|
||||
Map.Entry<?, ?> mapEntry = (Map.Entry<?, ?>) entry;
|
||||
Object key = mapEntry.getKey();
|
||||
if (keyType == null && key != null) {
|
||||
keyType = key.getClass();
|
||||
}
|
||||
Object value = mapEntry.getValue();
|
||||
if (valueType == null && value != null) {
|
||||
valueType = value.getClass();
|
||||
}
|
||||
if (keyType!= null && valueType != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new TypeDescriptor[] { TypeDescriptor.valueOf(keyType), TypeDescriptor.valueOf(valueType) };
|
||||
}
|
||||
|
||||
public static List<?> asList(Object array) {
|
||||
return array != null ? new ArrayList(array) : null;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import static org.springframework.core.convert.support.ConversionUtils.getMapEntryTypes;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -44,13 +46,18 @@ final class MapToCollectionConverter implements GenericConverter {
|
||||
Map<?, ?> sourceMap = (Map<?, ?>) source;
|
||||
TypeDescriptor sourceKeyType = sourceType.getMapKeyTypeDescriptor();
|
||||
TypeDescriptor sourceValueType = sourceType.getMapValueTypeDescriptor();
|
||||
if (sourceKeyType == TypeDescriptor.NULL || sourceValueType == TypeDescriptor.NULL) {
|
||||
TypeDescriptor[] sourceEntryTypes = getMapEntryTypes(sourceMap);
|
||||
sourceKeyType = sourceEntryTypes[0];
|
||||
sourceValueType = sourceEntryTypes[1];
|
||||
}
|
||||
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
|
||||
boolean keysCompatible = false;
|
||||
if (targetElementType == TypeDescriptor.NULL || sourceKeyType.isAssignableTo(targetElementType)) {
|
||||
if (sourceKeyType != TypeDescriptor.NULL && sourceKeyType.isAssignableTo(targetElementType)) {
|
||||
keysCompatible = true;
|
||||
}
|
||||
boolean valuesCompatible = false;
|
||||
if (targetElementType == TypeDescriptor.NULL || sourceValueType.isAssignableTo(targetElementType)) {
|
||||
if (sourceValueType != TypeDescriptor.NULL || sourceValueType.isAssignableTo(targetElementType)) {
|
||||
valuesCompatible = true;
|
||||
}
|
||||
Collection target = CollectionFactory.createCollection(targetType.getType(), sourceMap.size());
|
||||
@@ -63,11 +70,10 @@ final class MapToCollectionConverter implements GenericConverter {
|
||||
+ converter.convertValue(mapEntry.getValue());
|
||||
target.add(property);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
for (Object value : sourceMap.values()) {
|
||||
target.add(value);
|
||||
}
|
||||
target.add(converter.convertValue(value));
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import static org.springframework.core.convert.support.ConversionUtils.getMapEntryTypes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.CollectionFactory;
|
||||
@@ -46,20 +48,22 @@ final class MapToMapConverter implements GenericConverter {
|
||||
if (targetKeyType == TypeDescriptor.NULL && targetValueType == TypeDescriptor.NULL) {
|
||||
return compatibleMapWithoutEntryConversion(sourceMap, targetType);
|
||||
}
|
||||
TypeDescriptor[] sourceEntryTypes = getMapEntryTypes(sourceMap);
|
||||
TypeDescriptor sourceKeyType = sourceEntryTypes[0];
|
||||
TypeDescriptor sourceValueType = sourceEntryTypes[1];
|
||||
TypeDescriptor sourceKeyType = sourceType.getMapKeyTypeDescriptor();
|
||||
TypeDescriptor sourceValueType = sourceType.getMapValueTypeDescriptor();
|
||||
if (sourceKeyType == TypeDescriptor.NULL || sourceValueType == TypeDescriptor.NULL) {
|
||||
TypeDescriptor[] sourceEntryTypes = getMapEntryTypes(sourceMap);
|
||||
sourceKeyType = sourceEntryTypes[0];
|
||||
sourceValueType = sourceEntryTypes[1];
|
||||
}
|
||||
if (sourceKeyType == TypeDescriptor.NULL && sourceValueType == TypeDescriptor.NULL) {
|
||||
return compatibleMapWithoutEntryConversion(sourceMap, targetType);
|
||||
}
|
||||
boolean keysCompatible = false;
|
||||
if (sourceKeyType != TypeDescriptor.NULL && targetKeyType != TypeDescriptor.NULL
|
||||
&& sourceKeyType.isAssignableTo(targetKeyType)) {
|
||||
if (sourceKeyType != TypeDescriptor.NULL && sourceKeyType.isAssignableTo(targetKeyType)) {
|
||||
keysCompatible = true;
|
||||
}
|
||||
boolean valuesCompatible = false;
|
||||
if (sourceValueType != TypeDescriptor.NULL && targetValueType != TypeDescriptor.NULL
|
||||
&& sourceValueType.isAssignableTo(targetValueType)) {
|
||||
if (sourceValueType != TypeDescriptor.NULL && sourceValueType.isAssignableTo(targetValueType)) {
|
||||
valuesCompatible = true;
|
||||
}
|
||||
if (keysCompatible && valuesCompatible) {
|
||||
@@ -67,7 +71,7 @@ final class MapToMapConverter implements GenericConverter {
|
||||
}
|
||||
Map targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size());
|
||||
MapEntryConverter converter = new MapEntryConverter(sourceKeyType, sourceValueType, targetKeyType,
|
||||
targetValueType, keysCompatible, valuesCompatible, conversionService);
|
||||
targetValueType, keysCompatible, valuesCompatible, this.conversionService);
|
||||
for (Object entry : sourceMap.entrySet()) {
|
||||
Map.Entry sourceMapEntry = (Map.Entry) entry;
|
||||
Object targetKey = converter.convertKey(sourceMapEntry.getKey());
|
||||
@@ -77,26 +81,6 @@ final class MapToMapConverter implements GenericConverter {
|
||||
return targetMap;
|
||||
}
|
||||
|
||||
private TypeDescriptor[] getMapEntryTypes(Map<?, ?> sourceMap) {
|
||||
Class<?> keyType = null;
|
||||
Class<?> valueType = null;
|
||||
for (Object entry : sourceMap.entrySet()) {
|
||||
Map.Entry<?, ?> mapEntry = (Map.Entry<?, ?>) entry;
|
||||
Object key = mapEntry.getKey();
|
||||
if (keyType == null && key != null) {
|
||||
keyType = key.getClass();
|
||||
}
|
||||
Object value = mapEntry.getValue();
|
||||
if (valueType == null && value != null) {
|
||||
valueType = value.getClass();
|
||||
}
|
||||
if (mapEntry.getKey() != null && mapEntry.getValue() != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new TypeDescriptor[] { TypeDescriptor.valueOf(keyType), TypeDescriptor.valueOf(valueType) };
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<?, ?> compatibleMapWithoutEntryConversion(Map<?, ?> source, TypeDescriptor targetType) {
|
||||
if (targetType.getType().isAssignableFrom(source.getClass())) {
|
||||
|
||||
@@ -16,20 +16,23 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import static org.springframework.core.convert.support.ConversionUtils.getMapEntryTypes;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
|
||||
/**
|
||||
* Converts from a Ma to a single Object.
|
||||
* Converts from a Map to a single Object.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
final class MapToObjectConverter implements GenericConverter {
|
||||
|
||||
private static final String DELIMITER = " ";
|
||||
|
||||
private final GenericConversionService conversionService;
|
||||
|
||||
public MapToObjectConverter(GenericConversionService conversionService) {
|
||||
@@ -39,7 +42,7 @@ 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)) {
|
||||
@@ -51,61 +54,67 @@ final class MapToObjectConverter implements GenericConverter {
|
||||
if (targetType.typeEquals(String.class)) {
|
||||
TypeDescriptor sourceKeyType = sourceType.getMapKeyTypeDescriptor();
|
||||
TypeDescriptor sourceValueType = sourceType.getMapValueTypeDescriptor();
|
||||
if (sourceKeyType == TypeDescriptor.NULL || sourceValueType == TypeDescriptor.NULL) {
|
||||
TypeDescriptor[] sourceEntryTypes = getMapEntryTypes(sourceMap);
|
||||
sourceKeyType = sourceEntryTypes[0];
|
||||
sourceValueType = sourceEntryTypes[1];
|
||||
}
|
||||
boolean keysCompatible = false;
|
||||
if (sourceKeyType == TypeDescriptor.NULL || sourceKeyType.isAssignableTo(targetType)) {
|
||||
if (sourceKeyType != TypeDescriptor.NULL && sourceKeyType.isAssignableTo(targetType)) {
|
||||
keysCompatible = true;
|
||||
}
|
||||
boolean valuesCompatible = false;
|
||||
if (sourceValueType == TypeDescriptor.NULL || sourceValueType.isAssignableTo(targetType)) {
|
||||
if (sourceValueType != TypeDescriptor.NULL && sourceValueType.isAssignableTo(targetType)) {
|
||||
valuesCompatible = true;
|
||||
}
|
||||
Properties props = new Properties();
|
||||
if (keysCompatible && valuesCompatible) {
|
||||
StringBuilder string = new StringBuilder();
|
||||
int i = 0;
|
||||
for (Object entry : sourceMap.entrySet()) {
|
||||
Map.Entry<?, ?> mapEntry = (Map.Entry<?, ?>) entry;
|
||||
if (i > 0) {
|
||||
string.append(DELIMITER);
|
||||
}
|
||||
String property = mapEntry.getKey() + "=" + mapEntry.getValue();
|
||||
string.append(property);
|
||||
i++;
|
||||
props.setProperty((String) mapEntry.getKey(), (String) mapEntry.getValue());
|
||||
}
|
||||
return string.toString();
|
||||
return store(props);
|
||||
} 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()) {
|
||||
Map.Entry<?, ?> mapEntry = (Map.Entry<?, ?>) entry;
|
||||
if (i > 0) {
|
||||
string.append(DELIMITER);
|
||||
}
|
||||
Object key = converter.convertKey(mapEntry.getKey());
|
||||
Object value = converter.convertValue(mapEntry.getValue());
|
||||
String property = key + "=" + value;
|
||||
string.append(property);
|
||||
i++;
|
||||
props.setProperty((String) key, (String) value);
|
||||
}
|
||||
return string.toString();
|
||||
return store(props);
|
||||
}
|
||||
} else {
|
||||
Object firstValue = sourceMap.values().iterator().next();
|
||||
TypeDescriptor sourceValueType = sourceType.getMapValueTypeDescriptor();
|
||||
if (sourceValueType == TypeDescriptor.NULL) {
|
||||
sourceValueType = TypeDescriptor.forObject(firstValue);
|
||||
}
|
||||
boolean valuesCompatible = false;
|
||||
if (sourceValueType == TypeDescriptor.NULL || sourceValueType.isAssignableTo(targetType)) {
|
||||
if (sourceValueType != TypeDescriptor.NULL && sourceValueType.isAssignableTo(targetType)) {
|
||||
valuesCompatible = true;
|
||||
}
|
||||
if (valuesCompatible) {
|
||||
return sourceMap.values().iterator().next();
|
||||
return firstValue;
|
||||
} else {
|
||||
MapEntryConverter converter = new MapEntryConverter(sourceValueType, sourceValueType, targetType,
|
||||
targetType, true, valuesCompatible, this.conversionService);
|
||||
Object value = sourceMap.values().iterator().next();
|
||||
return converter.convertValue(value);
|
||||
return converter.convertValue(firstValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String store(Properties props) {
|
||||
try {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
props.store(os, null);
|
||||
return os.toString("ISO-8859-1");
|
||||
} catch (IOException e) {
|
||||
// Should never happen.
|
||||
throw new IllegalArgumentException("Failed to store [" + props + "] into String", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -31,11 +33,8 @@ final class ObjectToMapConverter implements GenericConverter {
|
||||
|
||||
private final GenericConversionService conversionService;
|
||||
|
||||
private final ArrayToMapConverter helperConverter;
|
||||
|
||||
public ObjectToMapConverter(GenericConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
this.helperConverter = new ArrayToMapConverter(conversionService);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -45,18 +44,17 @@ final class ObjectToMapConverter implements GenericConverter {
|
||||
}
|
||||
if (sourceType.typeEquals(String.class)) {
|
||||
String string = (String) source;
|
||||
String[] properties = string.split(" ");
|
||||
return this.helperConverter.convert(properties, TypeDescriptor.valueOf(String[].class), targetType);
|
||||
return this.conversionService.convert(loadProperties(string), TypeDescriptor.valueOf(Properties.class), targetType);
|
||||
} else {
|
||||
Map target = CollectionFactory.createMap(targetType.getType(), 1);
|
||||
TypeDescriptor targetKeyType = targetType.getMapKeyTypeDescriptor();
|
||||
TypeDescriptor targetValueType = targetType.getMapValueTypeDescriptor();
|
||||
boolean keysCompatible = false;
|
||||
if (targetKeyType == TypeDescriptor.NULL || sourceType.isAssignableTo(targetKeyType)) {
|
||||
if (sourceType != TypeDescriptor.NULL && sourceType.isAssignableTo(targetKeyType)) {
|
||||
keysCompatible = true;
|
||||
}
|
||||
boolean valuesCompatible = false;
|
||||
if (targetValueType == TypeDescriptor.NULL || sourceType.isAssignableTo(targetValueType)) {
|
||||
if (sourceType != TypeDescriptor.NULL && sourceType.isAssignableTo(targetValueType)) {
|
||||
valuesCompatible = true;
|
||||
}
|
||||
if (keysCompatible && valuesCompatible) {
|
||||
@@ -72,4 +70,16 @@ final class ObjectToMapConverter implements GenericConverter {
|
||||
}
|
||||
}
|
||||
|
||||
private Properties loadProperties(String string) {
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
// Must use the ISO-8859-1 encoding because Properties.load(stream) expects it.
|
||||
props.load(new ByteArrayInputStream(string.getBytes("ISO-8859-1")));
|
||||
return props;
|
||||
} catch (Exception e) {
|
||||
// Should never happen.
|
||||
throw new IllegalArgumentException("Failed to parse [" + string + "] into Properties", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -482,7 +482,9 @@ public class GenericConversionServiceTests {
|
||||
foo.put("1", "BAR");
|
||||
foo.put("2", "BAZ");
|
||||
String result = conversionService.convert(foo, String.class);
|
||||
assertEquals("1=BAR 2=BAZ", result);
|
||||
System.out.println(result);
|
||||
assertTrue(result.contains("1=BAR"));
|
||||
assertTrue(result.contains("2=BAZ"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -494,7 +496,8 @@ public class GenericConversionServiceTests {
|
||||
conversionService.addConverter(new ObjectToStringConverter());
|
||||
String result = (String) conversionService.convert(foo, new TypeDescriptor(getClass().getField("genericMap")),
|
||||
TypeDescriptor.valueOf(String.class));
|
||||
assertEquals("1=BAR 2=BAZ", result);
|
||||
assertTrue(result.contains("1=BAR"));
|
||||
assertTrue(result.contains("2=BAZ"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -626,7 +629,7 @@ public class GenericConversionServiceTests {
|
||||
@Test
|
||||
public void convertStringToMap() {
|
||||
conversionService.addGenericConverter(Object.class, Map.class, new ObjectToMapConverter(conversionService));
|
||||
Map result = conversionService.convert("foo=bar bar=baz baz=boop", Map.class);
|
||||
Map result = conversionService.convert(" foo=bar\n bar=baz\n baz=boop", Map.class);
|
||||
assertEquals("bar", result.get("foo"));
|
||||
assertEquals("baz", result.get("bar"));
|
||||
assertEquals("boop", result.get("baz"));
|
||||
@@ -637,7 +640,7 @@ public class GenericConversionServiceTests {
|
||||
conversionService.addGenericConverter(Object.class, Map.class, new ObjectToMapConverter(conversionService));
|
||||
conversionService.addConverterFactory(new StringToNumberConverterFactory());
|
||||
conversionService.addConverterFactory(new StringToEnumConverterFactory());
|
||||
Map result = (Map) conversionService.convert("1=BAR 2=BAZ", TypeDescriptor.valueOf(String.class),
|
||||
Map result = (Map) conversionService.convert("1=BAR\n 2=BAZ", TypeDescriptor.valueOf(String.class),
|
||||
new TypeDescriptor(getClass().getField("genericMap")));
|
||||
assertEquals(FooEnum.BAR, result.get(1));
|
||||
assertEquals(FooEnum.BAZ, result.get(2));
|
||||
@@ -646,9 +649,8 @@ public class GenericConversionServiceTests {
|
||||
@Test
|
||||
public void convertObjectToMap() {
|
||||
conversionService.addGenericConverter(Object.class, Map.class, new ObjectToMapConverter(conversionService));
|
||||
Map result = conversionService.convert("foo=bar bar=baz", Map.class);
|
||||
assertEquals("bar", result.get("foo"));
|
||||
assertEquals("baz", result.get("bar"));
|
||||
Map result = conversionService.convert(new Integer(3), Map.class);
|
||||
assertEquals(new Integer(3), result.get(new Integer(3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user