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-collections.git


The following commit(s) were added to refs/heads/master by this push:
     new 5d0895de5 Do not register a value collection until an add succeeds 
(#717)
5d0895de5 is described below

commit 5d0895de5bc00c5a025a17dbd51faa9398a176e2
Author: Naveed Khan <[email protected]>
AuthorDate: Wed Jul 22 14:04:02 2026 +0000

    Do not register a value collection until an add succeeds (#717)
    
    The wrapped collections returned by get(key) put a freshly created backing 
collection into the map before the add that fills it. addAll of an empty 
collection then leaves an empty collection mapped to the key, and 
WrappedList.add(int, value) leaves an empty list when the index is out of range 
and the insert throws. Store the new collection only after the add succeeds, 
matching put(K, V).
---
 .../collections4/multimap/AbstractListValuedMap.java        |  8 +++++---
 .../collections4/multimap/AbstractMultiValuedMap.java       | 10 +++++++---
 .../collections4/multimap/AbstractMultiValuedMapTest.java   | 13 +++++++++++++
 .../collections4/multimap/ArrayListValuedHashMapTest.java   | 12 ++++++++++++
 4 files changed, 37 insertions(+), 6 deletions(-)

diff --git 
a/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
 
b/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
index a073e4355..e2a4e865e 100644
--- 
a/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
+++ 
b/src/main/java/org/apache/commons/collections4/multimap/AbstractListValuedMap.java
@@ -126,10 +126,12 @@ public abstract class AbstractListValuedMap<K, V> extends 
AbstractMultiValuedMap
 
         @Override
         public void add(final int index, final V value) {
-            List<V> list = getMapping();
+            final List<V> list = getMapping();
             if (list == null) {
-                list = createCollection();
-                getMap().put(key, list);
+                final List<V> newList = createCollection();
+                newList.add(index, value);
+                getMap().put(key, newList);
+                return;
             }
             list.add(index, value);
         }
diff --git 
a/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
 
b/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
index 4f7323c0b..7973e378a 100644
--- 
a/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
+++ 
b/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
@@ -441,10 +441,14 @@ public abstract class AbstractMultiValuedMap<K, V> 
implements MultiValuedMap<K,
 
         @Override
         public boolean addAll(final Collection<? extends V> other) {
-            Collection<V> coll = getMapping();
+            final Collection<V> coll = getMapping();
             if (coll == null) {
-                coll = createCollection();
-                AbstractMultiValuedMap.this.map.put(key, coll);
+                final Collection<V> newColl = createCollection();
+                if (newColl.addAll(other)) {
+                    AbstractMultiValuedMap.this.map.put(key, newColl);
+                    return true;
+                }
+                return false;
             }
             return coll.addAll(other);
         }
diff --git 
a/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
 
b/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
index aaade2bcf..890bffdef 100644
--- 
a/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
+++ 
b/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
@@ -705,6 +705,19 @@ public abstract class AbstractMultiValuedMapTest<K, V> 
extends AbstractObjectTes
         assertTrue(col2.contains("v1_1"));
     }
 
+    @Test
+    @SuppressWarnings("unchecked")
+    void testAddAllThroughGetEmptyLeavesKeyAbsent() {
+        assumeTrue(isAddSupported());
+        resetEmpty();
+        final MultiValuedMap<K, V> map = getMap();
+        final Collection<V> col = map.get((K) "k0");
+        assertFalse(col.addAll(new ArrayList<>()));
+        assertFalse(map.containsKey("k0"));
+        assertFalse(map.keySet().contains("k0"));
+        assertEquals(0, map.size());
+    }
+
     /*void testRemoveViaGetCollectionRemove() {
         if (!isRemoveSupported()) {
             return;
diff --git 
a/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java
 
b/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java
index f65ee297c..18efa9361 100644
--- 
a/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java
+++ 
b/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java
@@ -19,6 +19,7 @@ package org.apache.commons.collections4.multimap;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.ArrayList;
@@ -117,6 +118,17 @@ public class ArrayListValuedHashMapTest<K, V> extends 
AbstractMultiValuedMapTest
         assertTrue(listMap.containsKey("A"));
     }
 
+    @Test
+    @SuppressWarnings("unchecked")
+    void testListValuedMapAddByIndexInvalidIndexLeavesKeyAbsent() {
+        final ListValuedMap<K, V> listMap = makeObject();
+        final List<V> list = listMap.get((K) "A");
+        assertThrows(IndexOutOfBoundsException.class, () -> list.add(1, (V) 
"a1"));
+        assertFalse(listMap.containsKey("A"));
+        assertTrue(listMap.get((K) "A").isEmpty());
+        assertEquals(0, listMap.size());
+    }
+
     @Test
     @SuppressWarnings("unchecked")
     void testListValuedMapAddViaListIterator() {

Reply via email to