Skip to content

Commit 75200cf

Browse files
committed
[COLLECTIONS-881] MultiMapUtils.getXXX ensure safe copy
1 parent c1f3cb2 commit 75200cf

2 files changed

Lines changed: 50 additions & 16 deletions

File tree

src/main/java/org/apache/commons/collections4/MultiMapUtils.java

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,11 @@ public static <K, V> Collection<V> getCollection(final MultiValuedMap<K, V> map,
102102
* @param <V> the value type
103103
* @param map the {@link MultiValuedMap} to use
104104
* @param key the key to look up
105-
* @return the Collection in the {@link MultiValuedMap} as Bag, or null if input map is null
105+
* @return a new Bag containing the values from the {@link MultiValuedMap}, or null if input map is null
106106
*/
107107
public static <K, V> Bag<V> getValuesAsBag(final MultiValuedMap<K, V> map, final K key) {
108108
if (map != null) {
109109
final Collection<V> col = map.get(key);
110-
if (col instanceof Bag) {
111-
return (Bag<V>) col;
112-
}
113110
return new HashBag<>(col);
114111
}
115112
return null;
@@ -122,37 +119,28 @@ public static <K, V> Bag<V> getValuesAsBag(final MultiValuedMap<K, V> map, final
122119
* @param <V> the value type
123120
* @param map the {@link MultiValuedMap} to use
124121
* @param key the key to look up
125-
* @return the Collection in the {@link MultiValuedMap} as List, or null if input map is null
122+
* @return a new List containing the values from the {@link MultiValuedMap}, or null if input map is null
126123
*/
127124
public static <K, V> List<V> getValuesAsList(final MultiValuedMap<K, V> map, final K key) {
128125
if (map != null) {
129126
final Collection<V> col = map.get(key);
130-
if (col instanceof List) {
131-
return (List<V>) col;
132-
}
133127
return new ArrayList<>(col);
134128
}
135129
return null;
136130
}
137131

138-
// TODO: review the getValuesAsXXX methods - depending on the actual MultiValuedMap type, changes
139-
// to the returned collection might update the backing map. This should be clarified and/or prevented.
140-
141132
/**
142133
* Gets a Set from {@code MultiValuedMap} in a null-safe manner.
143134
*
144135
* @param <K> the key type
145136
* @param <V> the value type
146137
* @param map the {@link MultiValuedMap} to use
147138
* @param key the key to look up
148-
* @return the Collection in the {@link MultiValuedMap} as Set, or null if input map is null
139+
* @return a new Set containing the values from the {@link MultiValuedMap}, or null if input map is null
149140
*/
150141
public static <K, V> Set<V> getValuesAsSet(final MultiValuedMap<K, V> map, final K key) {
151142
if (map != null) {
152143
final Collection<V> col = map.get(key);
153-
if (col instanceof Set) {
154-
return (Set<V>) col;
155-
}
156144
return new HashSet<>(col);
157145
}
158146
return null;

src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
*/
1717
package org.apache.commons.collections4;
1818

19+
import static org.easymock.EasyMock.expect;
20+
import static org.easymock.EasyMock.replay;
21+
import static org.easymock.EasyMock.verify;
22+
import static org.easymock.EasyMock.createMock;
1923
import static org.junit.jupiter.api.Assertions.assertEquals;
2024
import static org.junit.jupiter.api.Assertions.assertFalse;
2125
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -28,6 +32,7 @@
2832
import java.util.List;
2933
import java.util.Set;
3034

35+
import org.apache.commons.collections4.bag.HashBag;
3136
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
3237
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
3338
import org.apache.commons.collections4.multimap.LinkedHashSetValuedLinkedHashMap;
@@ -105,7 +110,7 @@ void testGetValuesAsList() {
105110

106111
@Test
107112
void testGetValuesAsSet() {
108-
assertNull(MultiMapUtils.getValuesAsList(null, "key1"));
113+
assertNull(MultiMapUtils.getValuesAsSet(null, "key1"));
109114

110115
final String[] values = { "v1", "v2", "v3" };
111116
final MultiValuedMap<String, String> map = new ArrayListValuedHashMap<>();
@@ -118,6 +123,47 @@ void testGetValuesAsSet() {
118123
assertEquals(new HashSet<>(Arrays.asList(values)), set);
119124
}
120125

126+
@Test
127+
void testGetValuesAsBagIsSafeCopy() {
128+
final String[] values = { "v1", "v2", "v3" };
129+
final MultiValuedMap<String, String> mockMap = createMock(MultiValuedMap.class);
130+
final Bag<String> bagToReturn = new HashBag<>();
131+
bagToReturn.addAll(Arrays.asList(values));
132+
expect(mockMap.get("key1")).andReturn(bagToReturn);
133+
replay(mockMap);
134+
135+
final Bag<String> bag = MultiMapUtils.getValuesAsBag(mockMap, "key1");
136+
bag.add("v4");
137+
assertFalse(bagToReturn.contains("v4"));
138+
verify(mockMap);
139+
}
140+
141+
@Test
142+
void testGetValuesAsListIsSafeCopy() {
143+
final String[] values = { "v1", "v2", "v3" };
144+
final MultiValuedMap<String, String> map = new ArrayListValuedHashMap<>();
145+
for (final String val : values) {
146+
map.put("key1", val);
147+
}
148+
149+
final List<String> list = MultiMapUtils.getValuesAsList(map, "key1");
150+
list.add("v4");
151+
assertFalse(map.containsMapping("key1", "v4"));
152+
}
153+
154+
@Test
155+
void testGetValuesAsSetIsSafeCopy() {
156+
final String[] values = { "v1", "v2", "v3" };
157+
final MultiValuedMap<String, String> map = new HashSetValuedHashMap<>();
158+
for (final String val : values) {
159+
map.put("key1", val);
160+
}
161+
162+
final Set<String> set = MultiMapUtils.getValuesAsSet(map, "key1");
163+
set.add("v4");
164+
assertFalse(map.containsMapping("key1", "v4"));
165+
}
166+
121167
@Test
122168
void testInvert() {
123169
final HashSetValuedHashMap<String, String> usages = new HashSetValuedHashMap<>();

0 commit comments

Comments
 (0)