Apply array editor to collection of same element type as well
Closes gh-24845
This commit is contained in:
@@ -35,6 +35,7 @@ import org.springframework.core.convert.ConversionService;
|
|||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.NumberUtils;
|
import org.springframework.util.NumberUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
@@ -139,13 +140,17 @@ class TypeConverterDelegate {
|
|||||||
|
|
||||||
// Value not of required type?
|
// Value not of required type?
|
||||||
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
|
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
|
||||||
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
|
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType)) {
|
||||||
convertedValue instanceof String text) {
|
|
||||||
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
|
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
|
||||||
if (elementTypeDesc != null) {
|
if (elementTypeDesc != null) {
|
||||||
Class<?> elementType = elementTypeDesc.getType();
|
Class<?> elementType = elementTypeDesc.getType();
|
||||||
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
|
if (convertedValue instanceof String text) {
|
||||||
convertedValue = StringUtils.commaDelimitedListToStringArray(text);
|
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
|
||||||
|
convertedValue = StringUtils.commaDelimitedListToStringArray(text);
|
||||||
|
}
|
||||||
|
if (editor == null && String.class != elementType) {
|
||||||
|
editor = findDefaultEditor(Array.newInstance(elementType, 0).getClass());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,11 +171,23 @@ class TypeConverterDelegate {
|
|||||||
}
|
}
|
||||||
else if (requiredType.isArray()) {
|
else if (requiredType.isArray()) {
|
||||||
// Array required -> apply appropriate conversion of elements.
|
// Array required -> apply appropriate conversion of elements.
|
||||||
if (convertedValue instanceof String text && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
|
if (convertedValue instanceof String text &&
|
||||||
|
Enum.class.isAssignableFrom(requiredType.getComponentType())) {
|
||||||
convertedValue = StringUtils.commaDelimitedListToStringArray(text);
|
convertedValue = StringUtils.commaDelimitedListToStringArray(text);
|
||||||
}
|
}
|
||||||
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
|
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
|
||||||
}
|
}
|
||||||
|
else if (convertedValue.getClass().isArray()) {
|
||||||
|
if (Array.getLength(convertedValue) == 1) {
|
||||||
|
convertedValue = Array.get(convertedValue, 0);
|
||||||
|
standardConversion = true;
|
||||||
|
}
|
||||||
|
else if (Collection.class.isAssignableFrom(requiredType)) {
|
||||||
|
convertedValue = convertToTypedCollection(CollectionUtils.arrayToList(convertedValue),
|
||||||
|
propertyName, requiredType, typeDescriptor);
|
||||||
|
standardConversion = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (convertedValue instanceof Collection<?> coll) {
|
else if (convertedValue instanceof Collection<?> coll) {
|
||||||
// Convert elements to target type, if determined.
|
// Convert elements to target type, if determined.
|
||||||
convertedValue = convertToTypedCollection(coll, propertyName, requiredType, typeDescriptor);
|
convertedValue = convertToTypedCollection(coll, propertyName, requiredType, typeDescriptor);
|
||||||
@@ -181,10 +198,6 @@ class TypeConverterDelegate {
|
|||||||
convertedValue = convertToTypedMap(map, propertyName, requiredType, typeDescriptor);
|
convertedValue = convertToTypedMap(map, propertyName, requiredType, typeDescriptor);
|
||||||
standardConversion = true;
|
standardConversion = true;
|
||||||
}
|
}
|
||||||
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
|
|
||||||
convertedValue = Array.get(convertedValue, 0);
|
|
||||||
standardConversion = true;
|
|
||||||
}
|
|
||||||
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
|
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
|
||||||
// We can stringify any primitive value...
|
// We can stringify any primitive value...
|
||||||
return (T) convertedValue.toString();
|
return (T) convertedValue.toString();
|
||||||
@@ -501,12 +514,11 @@ class TypeConverterDelegate {
|
|||||||
|
|
||||||
Collection<Object> convertedCopy;
|
Collection<Object> convertedCopy;
|
||||||
try {
|
try {
|
||||||
if (approximable) {
|
if (approximable && requiredType.isInstance(original)) {
|
||||||
convertedCopy = CollectionFactory.createApproximateCollection(original, original.size());
|
convertedCopy = CollectionFactory.createApproximateCollection(original, original.size());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
convertedCopy = (Collection<Object>)
|
convertedCopy = CollectionFactory.createCollection(requiredType, original.size());
|
||||||
ReflectionUtils.accessibleConstructor(requiredType).newInstance();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
@@ -576,12 +588,11 @@ class TypeConverterDelegate {
|
|||||||
|
|
||||||
Map<Object, Object> convertedCopy;
|
Map<Object, Object> convertedCopy;
|
||||||
try {
|
try {
|
||||||
if (approximable) {
|
if (approximable && requiredType.isInstance(original)) {
|
||||||
convertedCopy = CollectionFactory.createApproximateMap(original, original.size());
|
convertedCopy = CollectionFactory.createApproximateMap(original, original.size());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
convertedCopy = (Map<Object, Object>)
|
convertedCopy = CollectionFactory.createMap(requiredType, original.size());
|
||||||
ReflectionUtils.accessibleConstructor(requiredType).newInstance();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
|
|||||||
@@ -222,11 +222,12 @@ public class ClassPathXmlApplicationContextTests {
|
|||||||
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CONTEXT_WILDCARD);
|
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CONTEXT_WILDCARD);
|
||||||
Service service = ctx.getBean("service", Service.class);
|
Service service = ctx.getBean("service", Service.class);
|
||||||
assertThat(service.getResources()).containsExactlyInAnyOrder(contextA, contextB, contextC);
|
assertThat(service.getResources()).containsExactlyInAnyOrder(contextA, contextB, contextC);
|
||||||
|
assertThat(service.getResourceSet()).containsExactlyInAnyOrder(contextA, contextB, contextC);
|
||||||
ctx.close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void childWithProxy() throws Exception {
|
void childWithProxy() {
|
||||||
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CONTEXT_WILDCARD);
|
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CONTEXT_WILDCARD);
|
||||||
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext(
|
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext(
|
||||||
new String[] {CHILD_WITH_PROXY_CONTEXT}, ctx);
|
new String[] {CHILD_WITH_PROXY_CONTEXT}, ctx);
|
||||||
@@ -337,8 +338,7 @@ public class ClassPathXmlApplicationContextTests {
|
|||||||
};
|
};
|
||||||
ResourceTestBean resource1 = (ResourceTestBean) ctx.getBean("resource1");
|
ResourceTestBean resource1 = (ResourceTestBean) ctx.getBean("resource1");
|
||||||
ResourceTestBean resource2 = (ResourceTestBean) ctx.getBean("resource2");
|
ResourceTestBean resource2 = (ResourceTestBean) ctx.getBean("resource2");
|
||||||
boolean condition = resource1.getResource() instanceof ClassPathResource;
|
assertThat(resource1.getResource()).isInstanceOf(ClassPathResource.class);
|
||||||
assertThat(condition).isTrue();
|
|
||||||
StringWriter writer = new StringWriter();
|
StringWriter writer = new StringWriter();
|
||||||
FileCopyUtils.copy(new InputStreamReader(resource1.getResource().getInputStream()), writer);
|
FileCopyUtils.copy(new InputStreamReader(resource1.getResource().getInputStream()), writer);
|
||||||
assertThat(writer.toString()).isEqualTo("contexttest");
|
assertThat(writer.toString()).isEqualTo("contexttest");
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class ConversionServiceFactoryBeanTests {
|
|||||||
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(fileName, getClass());
|
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(fileName, getClass());
|
||||||
ResourceTestBean tb = ctx.getBean("resourceTestBean", ResourceTestBean.class);
|
ResourceTestBean tb = ctx.getBean("resourceTestBean", ResourceTestBean.class);
|
||||||
assertThat(resourceClass.isInstance(tb.getResource())).isTrue();
|
assertThat(resourceClass.isInstance(tb.getResource())).isTrue();
|
||||||
assertThat(tb.getResourceArray()).isNotEmpty();
|
assertThat(tb.getResourceArray()).hasSize(1);
|
||||||
assertThat(resourceClass.isInstance(tb.getResourceArray()[0])).isTrue();
|
assertThat(resourceClass.isInstance(tb.getResourceArray()[0])).isTrue();
|
||||||
assertThat(tb.getResourceMap()).hasSize(1);
|
assertThat(tb.getResourceMap()).hasSize(1);
|
||||||
assertThat(resourceClass.isInstance(tb.getResourceMap().get("key1"))).isTrue();
|
assertThat(resourceClass.isInstance(tb.getResourceMap().get("key1"))).isTrue();
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.context.support;
|
package org.springframework.context.support;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
@@ -37,6 +39,8 @@ public class Service implements ApplicationContextAware, MessageSourceAware, Dis
|
|||||||
|
|
||||||
private Resource[] resources;
|
private Resource[] resources;
|
||||||
|
|
||||||
|
private Set<Resource> resourceSet;
|
||||||
|
|
||||||
private boolean properlyDestroyed = false;
|
private boolean properlyDestroyed = false;
|
||||||
|
|
||||||
|
|
||||||
@@ -65,6 +69,14 @@ public class Service implements ApplicationContextAware, MessageSourceAware, Dis
|
|||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setResourceSet(Set<Resource> resourceSet) {
|
||||||
|
this.resourceSet = resourceSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Resource> getResourceSet() {
|
||||||
|
return resourceSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
<bean name="service" class="org.springframework.context.support.Service">
|
<bean name="service" class="org.springframework.context.support.Service">
|
||||||
<property name="resources" value="/org/springframework/context/support/test/context*.xml"/>
|
<property name="resources" value="/org/springframework/context/support/test/context*.xml"/>
|
||||||
|
<property name="resourceSet" value="/org/springframework/context/support/test/context*.xml"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean name="service2" class="org.springframework.context.support.Service" autowire="byName" depends-on="service">
|
<bean name="service2" class="org.springframework.context.support.Service" autowire="byName" depends-on="service">
|
||||||
|
|||||||
Reference in New Issue
Block a user