collection/array conversion returns original collection if possible (SPR-8538); backported smarter element conversion check from 3.1
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -22,7 +22,7 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -32,7 +32,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
final class ArrayToArrayConverter implements GenericConverter {
|
||||
final class ArrayToArrayConverter implements ConditionalGenericConverter {
|
||||
|
||||
private final CollectionToArrayConverter helperConverter;
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ final class ArrayToCollectionConverter implements ConditionalGenericConverter {
|
||||
}
|
||||
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
|
||||
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -16,40 +16,48 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Converts an Array to an Object by returning the first array element after converting it to the desired targetType.
|
||||
* This implementation first adapts the source Array to a List, then delegates to {@link CollectionToObjectConverter} to perform the target Object conversion.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
final class ArrayToObjectConverter implements ConditionalGenericConverter {
|
||||
|
||||
private final CollectionToObjectConverter helperConverter;
|
||||
private final ConversionService conversionService;
|
||||
|
||||
public ArrayToObjectConverter(ConversionService conversionService) {
|
||||
this.helperConverter = new CollectionToObjectConverter(conversionService);
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
return Collections.singleton(new ConvertiblePair(Object[].class, Object.class));
|
||||
}
|
||||
|
||||
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.helperConverter.matches(sourceType, targetType);
|
||||
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType, this.conversionService);
|
||||
}
|
||||
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.helperConverter.convert(Arrays.asList(ObjectUtils.toObjectArray(source)), sourceType, targetType);
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
if (sourceType.isAssignableTo(targetType)) {
|
||||
return source;
|
||||
}
|
||||
if (Array.getLength(source) == 0) {
|
||||
return null;
|
||||
}
|
||||
Object firstElement = Array.get(source, 0);
|
||||
return this.conversionService.convert(firstElement, sourceType.getElementTypeDescriptor(firstElement), targetType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -49,7 +49,7 @@ final class CollectionToArrayConverter implements ConditionalGenericConverter {
|
||||
}
|
||||
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
|
||||
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService);
|
||||
}
|
||||
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
|
||||
@@ -49,9 +49,9 @@ final class CollectionToCollectionConverter implements ConditionalGenericConvert
|
||||
}
|
||||
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor());
|
||||
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), conversionService);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (source == null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -43,13 +43,16 @@ final class CollectionToObjectConverter implements ConditionalGenericConverter {
|
||||
}
|
||||
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType);
|
||||
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType, this.conversionService);
|
||||
}
|
||||
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
if (sourceType.isAssignableTo(targetType)) {
|
||||
return source;
|
||||
}
|
||||
Collection<?> sourceCollection = (Collection<?>) source;
|
||||
if (sourceCollection.size() == 0) {
|
||||
return null;
|
||||
@@ -58,4 +61,4 @@ final class CollectionToObjectConverter implements ConditionalGenericConverter {
|
||||
return this.conversionService.convert(firstElement, sourceType.getElementTypeDescriptor(firstElement), targetType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -45,7 +45,7 @@ final class CollectionToStringConverter implements ConditionalGenericConverter {
|
||||
}
|
||||
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.conversionService.canConvert(sourceType.getElementTypeDescriptor(), targetType);
|
||||
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType, this.conversionService);
|
||||
}
|
||||
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
@@ -56,18 +56,17 @@ final class CollectionToStringConverter implements ConditionalGenericConverter {
|
||||
if (sourceCollection.size() == 0) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder string = new StringBuilder();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int i = 0;
|
||||
for (Object sourceElement : sourceCollection) {
|
||||
if (i > 0) {
|
||||
string.append(DELIMITER);
|
||||
sb.append(DELIMITER);
|
||||
}
|
||||
Object targetElement = this.conversionService.convert(
|
||||
sourceElement, sourceType.getElementTypeDescriptor(sourceElement), targetType);
|
||||
string.append(targetElement);
|
||||
Object targetElement = this.conversionService.convert(sourceElement, sourceType.getElementTypeDescriptor(sourceElement), targetType);
|
||||
sb.append(targetElement);
|
||||
i++;
|
||||
}
|
||||
return string.toString();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
|
||||
@@ -43,24 +42,27 @@ abstract class ConversionUtils {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
public static boolean canConvertElements(TypeDescriptor sourceElementType, TypeDescriptor targetElementType, ConversionService conversionService) {
|
||||
if (targetElementType == null) {
|
||||
// yes
|
||||
return true;
|
||||
}
|
||||
if (sourceElementType == null) {
|
||||
// maybe
|
||||
return true;
|
||||
}
|
||||
if (conversionService.canConvert(sourceElementType, targetElementType)) {
|
||||
// yes
|
||||
return true;
|
||||
}
|
||||
else if (sourceElementType.getType().isAssignableFrom(targetElementType.getType())) {
|
||||
// maybe;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// no;
|
||||
return false;
|
||||
}
|
||||
return new TypeDescriptor[] { TypeDescriptor.valueOf(keyType), TypeDescriptor.valueOf(valueType) };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -44,7 +44,7 @@ final class ObjectToArrayConverter implements ConditionalGenericConverter {
|
||||
}
|
||||
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
|
||||
return ConversionUtils.canConvertElements(sourceType, targetType.getElementTypeDescriptor(), this.conversionService);
|
||||
}
|
||||
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
@@ -57,4 +57,4 @@ final class ObjectToArrayConverter implements ConditionalGenericConverter {
|
||||
return target;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -46,7 +46,7 @@ final class ObjectToCollectionConverter implements ConditionalGenericConverter {
|
||||
}
|
||||
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
|
||||
return ConversionUtils.canConvertElements(sourceType, targetType.getElementTypeDescriptor(), this.conversionService);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -36,24 +36,23 @@ final class StringToBooleanConverter implements Converter<String, Boolean> {
|
||||
|
||||
static {
|
||||
trueValues.add("true");
|
||||
falseValues.add("false");
|
||||
|
||||
trueValues.add("on");
|
||||
falseValues.add("off");
|
||||
|
||||
trueValues.add("yes");
|
||||
falseValues.add("no");
|
||||
|
||||
trueValues.add("1");
|
||||
|
||||
falseValues.add("false");
|
||||
falseValues.add("off");
|
||||
falseValues.add("no");
|
||||
falseValues.add("0");
|
||||
}
|
||||
|
||||
public Boolean convert(String source) {
|
||||
String value = source.trim();
|
||||
if (value.length() == 0) {
|
||||
if ("".equals(value)) {
|
||||
return null;
|
||||
}
|
||||
else if (trueValues.contains(value)) {
|
||||
value = value.toLowerCase();
|
||||
if (trueValues.contains(value)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
else if (falseValues.contains(value)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.AbstractList;
|
||||
@@ -33,13 +34,17 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.ConverterNotFoundException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Keith Donald
|
||||
@@ -75,6 +80,9 @@ public class DefaultConversionTests {
|
||||
assertEquals(Boolean.valueOf(true), conversionService.convert("on", Boolean.class));
|
||||
assertEquals(Boolean.valueOf(true), conversionService.convert("yes", Boolean.class));
|
||||
assertEquals(Boolean.valueOf(true), conversionService.convert("1", Boolean.class));
|
||||
assertEquals(Boolean.valueOf(true), conversionService.convert("TRUE", Boolean.class));
|
||||
assertEquals(Boolean.valueOf(true), conversionService.convert("ON", Boolean.class));
|
||||
assertEquals(Boolean.valueOf(true), conversionService.convert("YES", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -83,6 +91,9 @@ public class DefaultConversionTests {
|
||||
assertEquals(Boolean.valueOf(false), conversionService.convert("off", Boolean.class));
|
||||
assertEquals(Boolean.valueOf(false), conversionService.convert("no", Boolean.class));
|
||||
assertEquals(Boolean.valueOf(false), conversionService.convert("0", Boolean.class));
|
||||
assertEquals(Boolean.valueOf(false), conversionService.convert("FALSE", Boolean.class));
|
||||
assertEquals(Boolean.valueOf(false), conversionService.convert("OFF", Boolean.class));
|
||||
assertEquals(Boolean.valueOf(false), conversionService.convert("NO", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -286,6 +297,24 @@ public class DefaultConversionTests {
|
||||
assertEquals(new Integer("3"), result.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpr7766() throws Exception {
|
||||
ConverterRegistry registry = ((ConverterRegistry) conversionService);
|
||||
registry.addConverter(new ColorConverter());
|
||||
List<Color> colors = (List<Color>) conversionService.convert(new String[] { "ffffff", "#000000" }, TypeDescriptor.valueOf(String[].class), new TypeDescriptor(new MethodParameter(getClass().getMethod("handlerMethod", List.class), 0)));
|
||||
assertEquals(2, colors.size());
|
||||
assertEquals(Color.WHITE, colors.get(0));
|
||||
assertEquals(Color.BLACK, colors.get(1));
|
||||
}
|
||||
|
||||
public class ColorConverter implements Converter<String, Color> {
|
||||
public Color convert(String source) { if (!source.startsWith("#")) source = "#" + source; return Color.decode(source); }
|
||||
}
|
||||
|
||||
public void handlerMethod(List<Color> color) {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertArrayToCollectionImpl() {
|
||||
LinkedList<?> result = conversionService.convert(new String[] { "1", "2", "3" }, LinkedList.class);
|
||||
@@ -357,7 +386,7 @@ public class DefaultConversionTests {
|
||||
@Test
|
||||
public void convertArrayToObject() {
|
||||
Object[] array = new Object[] { 3L };
|
||||
Object result = conversionService.convert(array, Object.class);
|
||||
Object result = conversionService.convert(array, Long.class);
|
||||
assertEquals(3L, result);
|
||||
}
|
||||
|
||||
@@ -368,6 +397,13 @@ public class DefaultConversionTests {
|
||||
assertEquals(new Integer(3), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertArrayToObjectAssignableTargetType() {
|
||||
Long[] array = new Long[] { 3L };
|
||||
Long[] result = (Long[]) conversionService.convert(array, Object.class);
|
||||
assertEquals(array, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertObjectToArray() {
|
||||
Object[] result = conversionService.convert(3L, Object[].class);
|
||||
@@ -460,6 +496,14 @@ public class DefaultConversionTests {
|
||||
assertEquals(new Integer(3), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertCollectionToObjectAssignableTarget() throws Exception {
|
||||
Collection<String> source = new ArrayList<String>();
|
||||
source.add("foo");
|
||||
Object result = conversionService.convert(source, TypeDescriptor.forObject(source), new TypeDescriptor(getClass().getField("assignableTarget")));
|
||||
assertEquals(source, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertObjectToCollection() {
|
||||
List<String> result = (List<String>) conversionService.convert(3L, List.class);
|
||||
@@ -620,6 +664,8 @@ public class DefaultConversionTests {
|
||||
conversionService.convert(new Long(3), SSN.class);
|
||||
}
|
||||
|
||||
public Object assignableTarget;
|
||||
|
||||
private static class SSN {
|
||||
|
||||
private String value;
|
||||
@@ -708,4 +754,5 @@ public class DefaultConversionTests {
|
||||
return new TestEntity(id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user