[ 
https://issues.apache.org/jira/browse/COLLECTIONS-892?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Gary D. Gregory resolved COLLECTIONS-892.
-----------------------------------------
    Fix Version/s: 4.6.0
       Resolution: Fixed

> IndexedCollection.add() mutates the decorated collection before rejecting a 
> duplicate unique index key
> ------------------------------------------------------------------------------------------------------
>
>                 Key: COLLECTIONS-892
>                 URL: https://issues.apache.org/jira/browse/COLLECTIONS-892
>             Project: Commons Collections
>          Issue Type: Bug
>          Components: Collection
>            Reporter: Ruiqi Dong
>            Priority: Minor
>             Fix For: 4.6.0
>
>
> *Summary*
> For `uniqueIndexedCollection(...)`, adding an object whose transformed key 
> already exists is supposed to fail with `IllegalArgumentException`.
> The current implementation appends the object to the decorated collection 
> first, and only afterwards checks the uniqueness constraint while updating 
> the index. If the key collides, the exception is thrown too late: the 
> decorated collection has already been mutated.
>  
> *Affected code*
> File: 
> `src/main/java/org/apache/commons/collections4/collection/IndexedCollection.java`
> {code:java}
> @Override
> public boolean add(final C object) {
>     final boolean added = super.add(object);
>     if (added) {
>         addToIndex(object);
>     }
>     return added;
> }
> private void addToIndex(final C object) {
>     final K key = keyTransformer.apply(object);
>     if (uniqueIndex && index.containsKey(key)) {
>         throw new IllegalArgumentException("Duplicate key in uniquely indexed 
> collection.");
>     }
>     index.put(key, object);
> } {code}
> *Reproducer*
> Add the following test to 
> `src/test/java/org/apache/commons/collections4/collection/IndexedCollectionTest.java`:
> {code:java}
> @Test
> void testFailedUniqueAddDoesNotPolluteDecoratedCollection() {
>     final IndexedCollection<Integer, String> indexed =
>             decorateUniqueCollection(new ArrayList<>());
>     indexed.add("01");
>     assertThrows(IllegalArgumentException.class, () -> indexed.add("1"));
>     assertEquals(1, indexed.size());
>     assertEquals(asList("01"), new ArrayList<>(indexed));
> } {code}
> Both `"01"` and `"1"` map to the same integer key `1`.
> Run:
> {code:java}
> mvn -q 
> -Dtest=org.apache.commons.collections4.collection.IndexedCollectionTest#testFailedUniqueAddDoesNotPolluteDecoratedCollection
>  test {code}
> Observed behavior
> {code:java}
> expected: <1> but was: <2> {code}
> *Expected behavior*
> After the duplicate-key `add(...)` fails, the collection should still contain 
> only the original element `"01"`.
> This is not merely about exception atomicity in the abstract. 
> `IndexedCollection` promises a unique index, but after a failed add:
> - the decorated collection contains both values
> - the index contains only the original key mapping
> So the collection and its index immediately diverge, and the uniqueness 
> guarantee is no longer reflected by the actual collection state.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to