diff --git a/src/main/java/org/apache/commons/collections4/functors/SwitchTransformer.java b/src/main/java/org/apache/commons/collections4/functors/SwitchTransformer.java index 213d1d5b95..a1dd1b6239 100644 --- a/src/main/java/org/apache/commons/collections4/functors/SwitchTransformer.java +++ b/src/main/java/org/apache/commons/collections4/functors/SwitchTransformer.java @@ -17,6 +17,7 @@ package org.apache.commons.collections4.functors; import java.io.Serializable; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -63,9 +64,10 @@ public static Transformer switchTransformer( if (map.isEmpty()) { return ConstantTransformer.nullTransformer(); } - // convert to array like this to guarantee iterator() ordering - final Transformer defaultTransformer = map.remove(null); - final int size = map.size(); + // copy so the caller's map is not mutated; LinkedHashMap preserves iterator() ordering + final Map, Transformer> entries = new LinkedHashMap<>(map); + final Transformer defaultTransformer = entries.remove(null); + final int size = entries.size(); if (size == 0) { return (Transformer) (defaultTransformer == null ? ConstantTransformer.nullTransformer() : defaultTransformer); @@ -73,8 +75,8 @@ public static Transformer switchTransformer( final Transformer[] transformers = new Transformer[size]; final Predicate[] preds = new Predicate[size]; int i = 0; - for (final Map.Entry, - ? extends Transformer> entry : map.entrySet()) { + for (final Map.Entry, + Transformer> entry : entries.entrySet()) { preds[i] = entry.getKey(); transformers[i] = entry.getValue(); i++; diff --git a/src/test/java/org/apache/commons/collections4/functors/SwitchTransformerMapMutatesTest.java b/src/test/java/org/apache/commons/collections4/functors/SwitchTransformerMapMutatesTest.java new file mode 100644 index 0000000000..3cde120689 --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/functors/SwitchTransformerMapMutatesTest.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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.apache.commons.collections4.functors; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.collections4.Predicate; +import org.apache.commons.collections4.Transformer; +import org.junit.jupiter.api.Test; + +/** + * Tests that SwitchTransformer.switchTransformer(Map) does not mutate the input map. + */ +class SwitchTransformerMapMutatesTest { + + /** + * Tests that switchTransformer(Map) does NOT mutate the input map by removing the null key. + */ + @Test + void testSwitchTransformerMapDoesNotMutateInput() { + final Transformer defaultTransformer = ConstantTransformer.constantTransformer("default"); + final Transformer transformer = ConstantTransformer.constantTransformer("value"); + final Predicate predicate = NullPredicate.nullPredicate(); + final Map, Transformer> map = new LinkedHashMap<>(); + map.put(null, defaultTransformer); + map.put(predicate, transformer); + final int sizeBefore = map.size(); + assertEquals(2, sizeBefore); + // Call the factory method + SwitchTransformer.switchTransformer(map); + // The map should NOT have been mutated - null key must still be present + final int sizeAfter = map.size(); + assertEquals(sizeBefore, sizeAfter, "switchTransformer must not mutate the input map; expected size"); + } +}