This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-beanutils.git


The following commit(s) were added to refs/heads/master by this push:
     new 0a253c99 Record element type of first element in 
LazyDynaList.transform (#434)
0a253c99 is described below

commit 0a253c99aed69835032c2d36172a27e3aea13ef3
Author: Naveed Khan <[email protected]>
AuthorDate: Sat Aug 22 20:15:23 2026 +0000

    Record element type of first element in LazyDynaList.transform (#434)
    
    * record element type of first element in LazyDynaList.transform
    
    * add tests for untyped list first-element paths
---
 .../apache/commons/beanutils2/LazyDynaList.java    |  13 +-
 .../commons/beanutils2/LazyDynaListTest.java       | 132 +++++++++++++++++++++
 2 files changed, 144 insertions(+), 1 deletion(-)

diff --git a/src/main/java/org/apache/commons/beanutils2/LazyDynaList.java 
b/src/main/java/org/apache/commons/beanutils2/LazyDynaList.java
index 28eb53db..1f296781 100644
--- a/src/main/java/org/apache/commons/beanutils2/LazyDynaList.java
+++ b/src/main/java/org/apache/commons/beanutils2/LazyDynaList.java
@@ -659,7 +659,18 @@ public class LazyDynaList extends ArrayList<Object> {
 
         // Check the new element type, matches all the
         // other elements in the List
-        if (elementType != null && !newElementType.equals(elementType)) {
+        if (elementType == null) {
+            // The first element populated defines the element type (see class
+            // Javadoc). Record it so later elements are type-checked and
+            // toArray()/toDynaBeanArray() know the element type.
+            this.elementType = newElementType;
+            this.elementDynaBeanType = newDynaBeanType;
+            if (WrapDynaBean.class.isAssignableFrom(newDynaBeanType)) {
+                this.wrapDynaClass = (WrapDynaClass) dynaBean.getDynaClass();
+            } else {
+                this.elementDynaClass = dynaBean.getDynaClass();
+            }
+        } else if (!newElementType.equals(elementType)) {
             throw new IllegalArgumentException("Element Type " + 
newElementType + " doesn't match other elements " + elementType);
         }
 
diff --git a/src/test/java/org/apache/commons/beanutils2/LazyDynaListTest.java 
b/src/test/java/org/apache/commons/beanutils2/LazyDynaListTest.java
index 8ed66837..52f1f67f 100644
--- a/src/test/java/org/apache/commons/beanutils2/LazyDynaListTest.java
+++ b/src/test/java/org/apache/commons/beanutils2/LazyDynaListTest.java
@@ -374,6 +374,138 @@ class LazyDynaListTest {
         lazyList.add(new HashMap<>());
     }
 
+    /**
+     * Test that the element type is set from the first element populated on 
an untyped List: a later
+     * element of a different type is rejected, and toArray() returns an array 
of the element type.
+     */
+    @Test
+    void testFirstElementSetsElementType() {
+        final LazyDynaList lazyList = new LazyDynaList();
+        lazyList.add(new HashMap<>());
+        assertThrows(IllegalArgumentException.class, () -> lazyList.add("a 
POJO String"),
+                "Element of a different type must be rejected once the type is 
set");
+
+        final TreeMap<String, Object>[] source = new TreeMap[2];
+        source[0] = new TreeMap<>();
+        source[0].put("key0", "val0");
+        source[1] = new TreeMap<>();
+        source[1].put("key1", "val1");
+        final LazyDynaList mapList = new LazyDynaList(source);
+        final TreeMap<?, ?>[] array = (TreeMap[]) mapList.toArray();
+        assertEquals(2, array.length);
+        assertEquals("val0", array[0].get("key0"));
+        assertEquals("val1", array[1].get("key1"));
+    }
+
+    /**
+     * Test that a POJO first element on an untyped List takes the 
WrapDynaBean path: toArray()
+     * returns an array of the POJO class and toDynaBeanArray() returns a 
WrapDynaBean[].
+     */
+    @Test
+    void testUntypedListPojoFirstElement() {
+        final LazyDynaList lazyList = new LazyDynaList();
+        final TestBean bean = new TestBean();
+        lazyList.add(bean);
+
+        final Object[] array = lazyList.toArray();
+        assertEquals(TestBean.class, array.getClass().getComponentType(), "Not 
TestBean[]");
+        assertSame(bean, array[0], "Wrong element");
+
+        final DynaBean[] dynaArray = lazyList.toDynaBeanArray();
+        assertEquals(WrapDynaBean.class, 
dynaArray.getClass().getComponentType(), "Not WrapDynaBean[]");
+        assertSame(bean, ((WrapDynaBean) dynaArray[0]).getInstance(), "Wrong 
wrapped instance");
+    }
+
+    /**
+     * Test that a DynaBean first element on an untyped List sets both the 
element type and the
+     * DynaBean type to the same DynaBean subclass.
+     */
+    @Test
+    void testUntypedListDynaBeanFirstElement() throws Exception {
+        final LazyDynaList lazyList = new LazyDynaList();
+        final DynaBean bean = basicDynaClass.newInstance();
+        lazyList.add(bean);
+
+        // elementType: toArray() returns an array of the DynaBean subclass
+        final Object[] array = lazyList.toArray();
+        assertEquals(BasicDynaBean.class, array.getClass().getComponentType(), 
"Not BasicDynaBean[]");
+        assertSame(bean, array[0], "Wrong element");
+
+        // elementDynaBeanType: toDynaBeanArray() returns the same subclass
+        final DynaBean[] dynaArray = lazyList.toDynaBeanArray();
+        assertEquals(BasicDynaBean.class, 
dynaArray.getClass().getComponentType(), "Not BasicDynaBean[]");
+        assertSame(bean, dynaArray[0], "Wrong element");
+    }
+
+    /**
+     * Test addAll(Collection) and addAll(int, Collection) on an untyped List: 
the type is set from
+     * the first element of the Collection and mismatched types are then 
rejected.
+     */
+    @Test
+    void testUntypedListAddAll() {
+        final List<Object> collection = new ArrayList<>();
+        for (int i = 0; i < 2; i++) {
+            final TreeMap<String, Object> map = new TreeMap<>();
+            map.put("prop" + i, "val" + i);
+            collection.add(map);
+        }
+
+        // addAll(Collection)
+        final LazyDynaList lazyList = new LazyDynaList();
+        lazyList.addAll(collection);
+        assertEquals(2, lazyList.size(), "1. check size");
+        TreeMap<?, ?>[] mapArray = (TreeMap[]) lazyList.toArray();
+        assertEquals("val0", mapArray[0].get("prop0"), "2. Map error");
+        assertEquals("val1", mapArray[1].get("prop1"), "3. Map error");
+        assertThrows(IllegalArgumentException.class, () -> lazyList.add(new 
TestBean()), "4. wrong type accepted");
+
+        // addAll(int, Collection) - grows the List to the insert position 
first
+        final LazyDynaList indexedList = new LazyDynaList();
+        indexedList.addAll(2, collection);
+        assertEquals(4, indexedList.size(), "5. check size");
+        mapArray = (TreeMap[]) indexedList.toArray();
+        assertEquals(4, mapArray.length, "6. check size");
+        assertEquals("val0", mapArray[2].get("prop0"), "7. Map error");
+        assertEquals("val1", mapArray[3].get("prop1"), "8. Map error");
+        assertThrows(IllegalArgumentException.class, () -> indexedList.add(new 
TestBean()), "9. wrong type accepted");
+    }
+
+    /**
+     * Test that get(index) grows an untyped List with the element type fixed 
by the first
+     * population.
+     */
+    @Test
+    void testUntypedListGrowAfterFirstElement() {
+        final LazyDynaList lazyList = new LazyDynaList();
+        final TreeMap<String, Object> map = new TreeMap<>();
+        map.put("prop", "val");
+        lazyList.add(map);
+
+        final Object grown = lazyList.get(2);
+        assertNotNull(grown, "DynaBean Not Created");
+        assertEquals(LazyDynaMap.class, grown.getClass(), "Not LazyDynaMap");
+        assertEquals(TreeMap.class, ((LazyDynaMap) grown).getMap().getClass(), 
"Wrong Map");
+        assertEquals(3, lazyList.size(), "check size");
+
+        final TreeMap<?, ?>[] mapArray = (TreeMap[]) lazyList.toArray();
+        assertEquals(3, mapArray.length, "check array size");
+        assertEquals("val", mapArray[0].get("prop"), "Map error");
+    }
+
+    /**
+     * Test toDynaBeanArray() type correctness for the untyped Map case.
+     */
+    @Test
+    void testUntypedListToDynaBeanArray() {
+        final LazyDynaList lazyList = new LazyDynaList();
+        lazyList.add(new HashMap<>());
+
+        final DynaBean[] dynaArray = lazyList.toDynaBeanArray();
+        assertEquals(LazyDynaMap.class, 
dynaArray.getClass().getComponentType(), "Not LazyDynaMap[]");
+        assertEquals(1, dynaArray.length, "check size");
+        assertEquals(HashMap.class, ((LazyDynaMap) 
dynaArray[0]).getMap().getClass(), "Wrong Map");
+    }
+
     /**
      * Test Pojo Create
      */

Reply via email to