diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index 646480055f..95d79fe711 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -613,16 +613,17 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra } private PropertyValue createDefaultPropertyValue(PropertyTokenHolder tokens) { - Class type = getPropertyTypeDescriptor(tokens.canonicalName).getType(); + TypeDescriptor desc = getPropertyTypeDescriptor(tokens.canonicalName); + Class type = desc.getType(); if (type == null) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + tokens.canonicalName, "Could not determine property type for auto-growing a default value"); } - Object defaultValue = newValue(type, tokens.canonicalName); + Object defaultValue = newValue(type, desc, tokens.canonicalName); return new PropertyValue(tokens.canonicalName, defaultValue); } - private Object newValue(Class type, String name) { + private Object newValue(Class type, TypeDescriptor desc, String name) { try { if (type.isArray()) { Class componentType = type.getComponentType(); @@ -637,17 +638,20 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra } } else if (Collection.class.isAssignableFrom(type)) { - return CollectionFactory.createCollection(type, 16); + TypeDescriptor elementDesc = (desc != null ? desc.getElementTypeDescriptor() : null); + return CollectionFactory.createCollection(type, (elementDesc != null ? elementDesc.getType() : null), 16); } else if (Map.class.isAssignableFrom(type)) { - return CollectionFactory.createMap(type, 16); + TypeDescriptor keyDesc = (desc != null ? desc.getMapKeyTypeDescriptor() : null); + return CollectionFactory.createMap(type, (keyDesc != null ? keyDesc.getType() : null), 16); } else { return type.newInstance(); } } catch (Exception ex) { - // TODO Root cause exception context is lost here... should we throw another exception type that preserves context instead? + // TODO: Root cause exception context is lost here; just exception message preserved. + // Should we throw another exception type that preserves context instead? throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, "Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path: " + ex); } @@ -860,7 +864,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra Object newArray = Array.newInstance(componentType, index + 1); System.arraycopy(array, 0, newArray, 0, length); for (int i = length; i < Array.getLength(newArray); i++) { - Array.set(newArray, i, newValue(componentType, name)); + Array.set(newArray, i, newValue(componentType, null, name)); } // TODO this is not efficient because conversion may create a copy ... set directly because we know it is assignable. setPropertyValue(name, newArray); @@ -882,7 +886,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra Class elementType = GenericCollectionTypeResolver.getCollectionReturnType(pd.getReadMethod(), nestingLevel); if (elementType != null) { for (int i = collection.size(); i < index + 1; i++) { - collection.add(newValue(elementType, name)); + collection.add(newValue(elementType, null, name)); } } } diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperEnumTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperEnumTests.java index 2b161c58e7..f97ec8f749 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperEnumTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperEnumTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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,8 +16,12 @@ package org.springframework.beans; +import java.util.LinkedHashMap; +import java.util.Map; + import org.junit.Test; +import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.tests.sample.beans.CustomEnum; import org.springframework.tests.sample.beans.GenericBean; @@ -121,4 +125,52 @@ public final class BeanWrapperEnumTests { assertTrue(gb.getCustomEnumSet().contains(CustomEnum.VALUE_2)); } + @Test + public void testStandardEnumSetWithMultipleValues() { + GenericBean gb = new GenericBean(); + BeanWrapper bw = new BeanWrapperImpl(gb); + bw.setConversionService(new DefaultConversionService()); + assertNull(gb.getStandardEnumSet()); + bw.setPropertyValue("standardEnumSet", new String[] {"VALUE_1", "VALUE_2"}); + assertEquals(2, gb.getStandardEnumSet().size()); + assertTrue(gb.getStandardEnumSet().contains(CustomEnum.VALUE_1)); + assertTrue(gb.getStandardEnumSet().contains(CustomEnum.VALUE_2)); + } + + @Test + public void testStandardEnumSetWithAutoGrowing() { + GenericBean gb = new GenericBean(); + BeanWrapper bw = new BeanWrapperImpl(gb); + bw.setAutoGrowNestedPaths(true); + assertNull(gb.getStandardEnumSet()); + bw.getPropertyValue("standardEnumSet.class"); + assertEquals(0, gb.getStandardEnumSet().size()); + } + + @Test + public void testStandardEnumMapWithMultipleValues() { + GenericBean gb = new GenericBean(); + BeanWrapper bw = new BeanWrapperImpl(gb); + bw.setConversionService(new DefaultConversionService()); + assertNull(gb.getStandardEnumMap()); + Map map = new LinkedHashMap(); + map.put("VALUE_1", 1); + map.put("VALUE_2", 2); + bw.setPropertyValue("standardEnumMap", map); + assertEquals(2, gb.getStandardEnumMap().size()); + assertEquals(new Integer(1), gb.getStandardEnumMap().get(CustomEnum.VALUE_1)); + assertEquals(new Integer(2), gb.getStandardEnumMap().get(CustomEnum.VALUE_2)); + } + + @Test + public void testStandardEnumMapWithAutoGrowing() { + GenericBean gb = new GenericBean(); + BeanWrapper bw = new BeanWrapperImpl(gb); + bw.setAutoGrowNestedPaths(true); + assertNull(gb.getStandardEnumMap()); + bw.setPropertyValue("standardEnumMap[VALUE_1]", 1); + assertEquals(1, gb.getStandardEnumMap().size()); + assertEquals(new Integer(1), gb.getStandardEnumMap().get(CustomEnum.VALUE_1)); + } + } diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericBean.java b/spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericBean.java index c192382a1b..e74e709cf6 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericBean.java +++ b/spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2014 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. @@ -19,6 +19,8 @@ package org.springframework.tests.sample.beans; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -67,6 +69,10 @@ public class GenericBean { private Set customEnumSet; + private EnumSet standardEnumSet; + + private EnumMap standardEnumMap; + private T genericProperty; private List genericListProperty; @@ -267,6 +273,22 @@ public class GenericBean { } } + public EnumSet getStandardEnumSet() { + return standardEnumSet; + } + + public void setStandardEnumSet(EnumSet standardEnumSet) { + this.standardEnumSet = standardEnumSet; + } + + public EnumMap getStandardEnumMap() { + return standardEnumMap; + } + + public void setStandardEnumMap(EnumMap standardEnumMap) { + this.standardEnumMap = standardEnumMap; + } + public static GenericBean createInstance(Set integerSet) { return new GenericBean(integerSet); } diff --git a/spring-core/src/main/java/org/springframework/core/CollectionFactory.java b/spring-core/src/main/java/org/springframework/core/CollectionFactory.java index 39994a3d76..f26737466d 100644 --- a/spring-core/src/main/java/org/springframework/core/CollectionFactory.java +++ b/spring-core/src/main/java/org/springframework/core/CollectionFactory.java @@ -140,8 +140,10 @@ public abstract class CollectionFactory { * Create the most appropriate collection for the given collection type. * @param collectionClass the desired type of the target Collection * @param elementType the collection's element type, or {@code null} if not known + * (note: only relevant for {@link EnumSet} creation) * @param capacity the initial capacity * @return the new Collection instance + * @since 4.1.3 * @see java.util.LinkedHashSet * @see java.util.TreeSet * @see java.util.EnumSet @@ -229,8 +231,10 @@ public abstract class CollectionFactory { * Create the most approximate map for the given map. * @param mapClass the desired type of the target Map * @param keyType the map's key type, or {@code null} if not known + * (note: only relevant for {@link EnumMap} creation) * @param capacity the initial capacity * @return the new Map instance + * @since 4.1.3 * @see java.util.LinkedHashMap * @see java.util.TreeMap * @see java.util.EnumMap