diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index dca277179f6c..adf512bff105 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -678,7 +678,28 @@ else if (value instanceof Map) { Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor); value = map.get(convertedMapKey); } - else { + else if (value instanceof Iterable) { + // Apply index to Iterator in case of other Iterable implementation. + Iterable iterable = (Iterable) value; + int index = Integer.parseInt(key); + Iterator it = iterable.iterator(); + Object valueFound = null; + for (int j = 0; it.hasNext(); j++) { + Object elem = it.next(); + if (j == index) { + valueFound = elem; + break; + } + } + if (valueFound == null) { + throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, + "Cannot get element with index " + index + " from Iterable, " + + "accessed using property path '" + propertyName + "'"); + } else { + value = valueFound; + } + + } else { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Property referenced in indexed property path '" + propertyName + "' is neither an array nor a List nor a Set nor a Map; returned value was [" + value + "]"); diff --git a/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java b/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java index 14118896c078..747ae9186954 100644 --- a/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java @@ -56,6 +56,7 @@ import org.springframework.tests.sample.beans.IndexedTestBean; import org.springframework.tests.sample.beans.NumberTestBean; import org.springframework.tests.sample.beans.TestBean; +import org.springframework.tests.sample.beans.TestBeans; import org.springframework.util.StopWatch; import org.springframework.util.StringUtils; @@ -159,12 +160,14 @@ public void isReadableWritableForIndexedProperties() { assertTrue(accessor.isReadableProperty("list")); assertTrue(accessor.isReadableProperty("set")); assertTrue(accessor.isReadableProperty("map")); + assertTrue(accessor.isReadableProperty("myIterableType")); assertFalse(accessor.isReadableProperty("xxx")); assertTrue(accessor.isWritableProperty("array")); assertTrue(accessor.isWritableProperty("list")); assertTrue(accessor.isWritableProperty("set")); assertTrue(accessor.isWritableProperty("map")); + assertTrue(accessor.isWritableProperty("myIterableType")); assertFalse(accessor.isWritableProperty("xxx")); assertTrue(accessor.isReadableProperty("array[0]")); @@ -179,6 +182,7 @@ public void isReadableWritableForIndexedProperties() { assertTrue(accessor.isReadableProperty("map[key4][0].name")); assertTrue(accessor.isReadableProperty("map[key4][1]")); assertTrue(accessor.isReadableProperty("map[key4][1].name")); + assertTrue(accessor.isReadableProperty("myIterableType[0].name")); assertFalse(accessor.isReadableProperty("array[key1]")); assertTrue(accessor.isWritableProperty("array[0]")); @@ -193,6 +197,7 @@ public void isReadableWritableForIndexedProperties() { assertTrue(accessor.isWritableProperty("map[key4][0].name")); assertTrue(accessor.isWritableProperty("map[key4][1]")); assertTrue(accessor.isWritableProperty("map[key4][1].name")); + assertTrue(accessor.isReadableProperty("myIterableType[0].name")); assertFalse(accessor.isWritableProperty("array[key1]")); } @@ -1618,6 +1623,7 @@ public void getAndSetIndexedProperties() { assertEquals("name5", accessor.getPropertyValue("map[\"key.3\"].name")); assertEquals("nameX", accessor.getPropertyValue("map[key4][0].name")); assertEquals("nameY", accessor.getPropertyValue("map[key4][1].name")); + assertEquals("nameZ", accessor.getPropertyValue("myIterableType[0].name")); MutablePropertyValues pvs = new MutablePropertyValues(); pvs.add("array[0].name", "name5"); @@ -1630,6 +1636,7 @@ public void getAndSetIndexedProperties() { pvs.add("map['key.3'].name", "name0"); pvs.add("map[key4][0].name", "nameA"); pvs.add("map[key4][1].name", "nameB"); + pvs.add("myIterableType[0].name", "nameAZ"); accessor.setPropertyValues(pvs); assertEquals("name5", tb0.getName()); assertEquals("name4", tb1.getName()); @@ -1647,6 +1654,7 @@ public void getAndSetIndexedProperties() { assertEquals("name0", accessor.getPropertyValue("map['key.3'].name")); assertEquals("nameA", accessor.getPropertyValue("map[key4][0].name")); assertEquals("nameB", accessor.getPropertyValue("map[key4][1].name")); + assertEquals("nameAZ", accessor.getPropertyValue("myIterableType[0].name")); } @Test @@ -1661,6 +1669,7 @@ public void getAndSetIndexedPropertiesWithDirectAccess() { TestBean tb7 = ((TestBean) target.getSet().toArray()[1]); TestBean tb4 = ((TestBean) target.getMap().get("key1")); TestBean tb5 = ((TestBean) target.getMap().get("key2")); + TestBean tbZ = ((TestBean) target.getMyIterableType().iterator().next()); assertEquals(tb0, accessor.getPropertyValue("array[0]")); assertEquals(tb1, accessor.getPropertyValue("array[1]")); assertEquals(tb2, accessor.getPropertyValue("list[0]")); @@ -1671,6 +1680,8 @@ public void getAndSetIndexedPropertiesWithDirectAccess() { assertEquals(tb5, accessor.getPropertyValue("map[key2]")); assertEquals(tb4, accessor.getPropertyValue("map['key1']")); assertEquals(tb5, accessor.getPropertyValue("map[\"key2\"]")); + assertEquals(tb5, accessor.getPropertyValue("map[\"key2\"]")); + assertEquals(tbZ, accessor.getPropertyValue("myIterableType[0]")); MutablePropertyValues pvs = new MutablePropertyValues(); pvs.add("array[0]", tb5); diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/IndexedTestBean.java b/spring-beans/src/test/java/org/springframework/tests/sample/beans/IndexedTestBean.java index d319bbd0eda7..2a96c4a9e686 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/IndexedTestBean.java +++ b/spring-beans/src/test/java/org/springframework/tests/sample/beans/IndexedTestBean.java @@ -17,6 +17,7 @@ package org.springframework.tests.sample.beans; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -46,6 +47,7 @@ public class IndexedTestBean { private SortedMap sortedMap; + private TestBeans myIterableType; public IndexedTestBean() { this(true); @@ -68,6 +70,7 @@ public void populate() { TestBean tb7 = new TestBean("name7", 0); TestBean tbX = new TestBean("nameX", 0); TestBean tbY = new TestBean("nameY", 0); + TestBean tbZ = new TestBean("nameZ", 0); this.array = new TestBean[] {tb0, tb1}; this.list = new ArrayList(); this.list.add(tb2); @@ -83,6 +86,7 @@ public void populate() { list.add(tbX); list.add(tbY); this.map.put("key4", list); + this.myIterableType = new TestBeans(Arrays.asList(tbZ)); } @@ -142,4 +146,12 @@ public void setSortedMap(SortedMap sortedMap) { this.sortedMap = sortedMap; } + public TestBeans getMyIterableType() { + return myIterableType; + } + + public void setMyIterableType(TestBeans myIterableType) { + this.myIterableType = myIterableType; + } + } \ No newline at end of file diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBeans.java b/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBeans.java new file mode 100644 index 000000000000..ed9cd2d686d7 --- /dev/null +++ b/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBeans.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2015 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.tests.sample.beans; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +/** + * Simple model class to represent a collection of {@link TestBean} + * + * @author Tiago de Freitas Lima + * @since 4.2.1 + */ +public class TestBeans implements Iterable { + + private Collection beans; + + public TestBeans(Collection beans) { + this.beans = new ArrayList(beans); + } + + @Override + public Iterator iterator() { + return beans.iterator(); + } + +}