• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.collect;
18 
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.base.Function;
21 import com.google.common.base.Functions;
22 import com.google.common.collect.testing.MapInterfaceTest;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.Set;
28 import org.checkerframework.checker.nullness.qual.Nullable;
29 
30 /**
31  * Tests for {@link Maps#transformValues} when the backing map's views have iterators that don't
32  * support {@code remove()}.
33  *
34  * @author Jared Levy
35  */
36 @GwtCompatible
37 public class MapsTransformValuesUnmodifiableIteratorTest extends MapInterfaceTest<String, String> {
38   // TODO(jlevy): Move shared code of this class and MapsTransformValuesTest
39   // to a superclass.
40 
MapsTransformValuesUnmodifiableIteratorTest()41   public MapsTransformValuesUnmodifiableIteratorTest() {
42     super(true, true, false /*supportsPut*/, true, true, false);
43   }
44 
45   private static class UnmodifiableIteratorMap<K, V> extends ForwardingMap<K, V> {
46     final Map<K, V> delegate;
47 
UnmodifiableIteratorMap(Map<K, V> delegate)48     UnmodifiableIteratorMap(Map<K, V> delegate) {
49       this.delegate = delegate;
50     }
51 
52     @Override
delegate()53     protected Map<K, V> delegate() {
54       return delegate;
55     }
56 
57     @Override
keySet()58     public Set<K> keySet() {
59       return new ForwardingSet<K>() {
60         @Override
61         protected Set<K> delegate() {
62           return delegate.keySet();
63         }
64 
65         @Override
66         public Iterator<K> iterator() {
67           return Iterators.unmodifiableIterator(delegate.keySet().iterator());
68         }
69 
70         @Override
71         public boolean removeAll(Collection<?> c) {
72           return delegate.keySet().removeAll(c);
73         }
74 
75         @Override
76         public boolean retainAll(Collection<?> c) {
77           return delegate.keySet().retainAll(c);
78         }
79       };
80     }
81 
82     @Override
values()83     public Collection<V> values() {
84       return new ForwardingCollection<V>() {
85         @Override
86         protected Collection<V> delegate() {
87           return delegate.values();
88         }
89 
90         @Override
91         public Iterator<V> iterator() {
92           return Iterators.unmodifiableIterator(delegate.values().iterator());
93         }
94 
95         @Override
96         public boolean removeAll(Collection<?> c) {
97           return delegate.values().removeAll(c);
98         }
99 
100         @Override
101         public boolean retainAll(Collection<?> c) {
102           return delegate.values().retainAll(c);
103         }
104       };
105     }
106 
107     @Override
108     public Set<Entry<K, V>> entrySet() {
109       return new ForwardingSet<Entry<K, V>>() {
110         @Override
111         protected Set<Entry<K, V>> delegate() {
112           return delegate.entrySet();
113         }
114 
115         @Override
116         public Iterator<Entry<K, V>> iterator() {
117           return Iterators.unmodifiableIterator(delegate.entrySet().iterator());
118         }
119 
120         @Override
121         public boolean removeAll(Collection<?> c) {
122           return delegate.entrySet().removeAll(c);
123         }
124 
125         @Override
126         public boolean retainAll(Collection<?> c) {
127           return delegate.entrySet().retainAll(c);
128         }
129       };
130     }
131   }
132 
133   @Override
134   protected Map<String, String> makeEmptyMap() {
135     Map<String, Integer> underlying = Maps.newHashMap();
136     return Maps.transformValues(
137         new UnmodifiableIteratorMap<String, Integer>(underlying), Functions.toStringFunction());
138   }
139 
140   @Override
141   protected Map<String, String> makePopulatedMap() {
142     Map<String, Integer> underlying = Maps.newHashMap();
143     underlying.put("a", 1);
144     underlying.put("b", 2);
145     underlying.put("c", 3);
146     return Maps.transformValues(
147         new UnmodifiableIteratorMap<String, Integer>(underlying), Functions.toStringFunction());
148   }
149 
150   @Override
151   protected String getKeyNotInPopulatedMap() throws UnsupportedOperationException {
152     return "z";
153   }
154 
155   @Override
156   protected String getValueNotInPopulatedMap() throws UnsupportedOperationException {
157     return "26";
158   }
159 
160   /** Helper assertion comparing two maps */
161   private void assertMapsEqual(Map<?, ?> expected, Map<?, ?> map) {
162     assertEquals(expected, map);
163     assertEquals(expected.hashCode(), map.hashCode());
164     assertEquals(expected.entrySet(), map.entrySet());
165 
166     // Assert that expectedValues > mapValues and that
167     // mapValues > expectedValues; i.e. that expectedValues == mapValues.
168     Collection<?> expectedValues = expected.values();
169     Collection<?> mapValues = map.values();
170     assertEquals(expectedValues.size(), mapValues.size());
171     assertTrue(expectedValues.containsAll(mapValues));
172     assertTrue(mapValues.containsAll(expectedValues));
173   }
174 
175   public void testTransformEmptyMapEquality() {
176     Map<String, String> map =
177         Maps.transformValues(ImmutableMap.<String, Integer>of(), Functions.toStringFunction());
178     assertMapsEqual(Maps.newHashMap(), map);
179   }
180 
181   public void testTransformSingletonMapEquality() {
182     Map<String, String> map =
183         Maps.transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction());
184     Map<String, String> expected = ImmutableMap.of("a", "1");
185     assertMapsEqual(expected, map);
186     assertEquals(expected.get("a"), map.get("a"));
187   }
188 
189   public void testTransformIdentityFunctionEquality() {
190     Map<String, Integer> underlying = ImmutableMap.of("a", 1);
191     Map<String, Integer> map = Maps.transformValues(underlying, Functions.<Integer>identity());
192     assertMapsEqual(underlying, map);
193   }
194 
195   public void testTransformPutEntryIsUnsupported() {
196     Map<String, String> map =
197         Maps.transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction());
198     try {
199       map.put("b", "2");
200       fail();
201     } catch (UnsupportedOperationException expected) {
202     }
203 
204     try {
205       map.putAll(ImmutableMap.of("b", "2"));
206       fail();
207     } catch (UnsupportedOperationException expected) {
208     }
209 
210     try {
211       map.entrySet().iterator().next().setValue("one");
212       fail();
213     } catch (UnsupportedOperationException expected) {
214     }
215   }
216 
217   public void testTransformRemoveEntry() {
218     Map<String, Integer> underlying = Maps.newHashMap();
219     underlying.put("a", 1);
220     Map<String, String> map = Maps.transformValues(underlying, Functions.toStringFunction());
221     assertEquals("1", map.remove("a"));
222     assertNull(map.remove("b"));
223   }
224 
225   public void testTransformEqualityOfMapsWithNullValues() {
226     Map<String, String> underlying = Maps.newHashMap();
227     underlying.put("a", null);
228     underlying.put("b", "");
229 
230     Map<String, Boolean> map =
231         Maps.transformValues(
232             underlying,
233             new Function<String, Boolean>() {
234               @Override
235               public Boolean apply(@Nullable String from) {
236                 return from == null;
237               }
238             });
239     Map<String, Boolean> expected = ImmutableMap.of("a", true, "b", false);
240     assertMapsEqual(expected, map);
241     assertEquals(expected.get("a"), map.get("a"));
242     assertEquals(expected.containsKey("a"), map.containsKey("a"));
243     assertEquals(expected.get("b"), map.get("b"));
244     assertEquals(expected.containsKey("b"), map.containsKey("b"));
245     assertEquals(expected.get("c"), map.get("c"));
246     assertEquals(expected.containsKey("c"), map.containsKey("c"));
247   }
248 
249   public void testTransformReflectsUnderlyingMap() {
250     Map<String, Integer> underlying = Maps.newHashMap();
251     underlying.put("a", 1);
252     underlying.put("b", 2);
253     underlying.put("c", 3);
254     Map<String, String> map = Maps.transformValues(underlying, Functions.toStringFunction());
255     assertEquals(underlying.size(), map.size());
256 
257     underlying.put("d", 4);
258     assertEquals(underlying.size(), map.size());
259     assertEquals("4", map.get("d"));
260 
261     underlying.remove("c");
262     assertEquals(underlying.size(), map.size());
263     assertFalse(map.containsKey("c"));
264 
265     underlying.clear();
266     assertEquals(underlying.size(), map.size());
267   }
268 
269   public void testTransformChangesAreReflectedInUnderlyingMap() {
270     Map<String, Integer> underlying = Maps.newLinkedHashMap();
271     underlying.put("a", 1);
272     underlying.put("b", 2);
273     underlying.put("c", 3);
274     underlying.put("d", 4);
275     underlying.put("e", 5);
276     underlying.put("f", 6);
277     underlying.put("g", 7);
278     Map<String, String> map = Maps.transformValues(underlying, Functions.toStringFunction());
279 
280     map.remove("a");
281     assertFalse(underlying.containsKey("a"));
282 
283     Set<String> keys = map.keySet();
284     keys.remove("b");
285     assertFalse(underlying.containsKey("b"));
286 
287     Iterator<String> keyIterator = keys.iterator();
288     keyIterator.next();
289     keyIterator.remove();
290     assertFalse(underlying.containsKey("c"));
291 
292     Collection<String> values = map.values();
293     values.remove("4");
294     assertFalse(underlying.containsKey("d"));
295 
296     Iterator<String> valueIterator = values.iterator();
297     valueIterator.next();
298     valueIterator.remove();
299     assertFalse(underlying.containsKey("e"));
300 
301     Set<Entry<String, String>> entries = map.entrySet();
302     Entry<String, String> firstEntry = entries.iterator().next();
303     entries.remove(firstEntry);
304     assertFalse(underlying.containsKey("f"));
305 
306     Iterator<Entry<String, String>> entryIterator = entries.iterator();
307     entryIterator.next();
308     entryIterator.remove();
309     assertFalse(underlying.containsKey("g"));
310 
311     assertTrue(underlying.isEmpty());
312     assertTrue(map.isEmpty());
313     assertTrue(keys.isEmpty());
314     assertTrue(values.isEmpty());
315     assertTrue(entries.isEmpty());
316   }
317 
318   public void testTransformEquals() {
319     Map<String, Integer> underlying = ImmutableMap.of("a", 0, "b", 1, "c", 2);
320     Map<String, Integer> expected = Maps.transformValues(underlying, Functions.<Integer>identity());
321 
322     assertMapsEqual(expected, expected);
323 
324     Map<String, Integer> equalToUnderlying = Maps.newTreeMap();
325     equalToUnderlying.putAll(underlying);
326     Map<String, Integer> map =
327         Maps.transformValues(equalToUnderlying, Functions.<Integer>identity());
328     assertMapsEqual(expected, map);
329 
330     map =
331         Maps.transformValues(
332             ImmutableMap.of("a", 1, "b", 2, "c", 3),
333             new Function<Integer, Integer>() {
334               @Override
335               public Integer apply(Integer from) {
336                 return from - 1;
337               }
338             });
339     assertMapsEqual(expected, map);
340   }
341 
342   public void testTransformEntrySetContains() {
343     Map<String, Boolean> underlying = Maps.newHashMap();
344     underlying.put("a", null);
345     underlying.put("b", true);
346     underlying.put(null, true);
347 
348     Map<String, Boolean> map =
349         Maps.transformValues(
350             underlying,
351             new Function<Boolean, Boolean>() {
352               @Override
353               public Boolean apply(@Nullable Boolean from) {
354                 return (from == null) ? true : null;
355               }
356             });
357 
358     Set<Entry<String, Boolean>> entries = map.entrySet();
359     assertTrue(entries.contains(Maps.immutableEntry("a", true)));
360     assertTrue(entries.contains(Maps.immutableEntry("b", (Boolean) null)));
361     assertTrue(entries.contains(Maps.immutableEntry((String) null, (Boolean) null)));
362 
363     assertFalse(entries.contains(Maps.immutableEntry("c", (Boolean) null)));
364     assertFalse(entries.contains(Maps.immutableEntry((String) null, true)));
365   }
366 
367   @Override
368   public void testKeySetRemoveAllNullFromEmpty() {
369     try {
370       super.testKeySetRemoveAllNullFromEmpty();
371     } catch (RuntimeException tolerated) {
372       // GWT's HashMap.keySet().removeAll(null) doesn't throws NPE.
373     }
374   }
375 
376   @Override
377   public void testEntrySetRemoveAllNullFromEmpty() {
378     try {
379       super.testEntrySetRemoveAllNullFromEmpty();
380     } catch (RuntimeException tolerated) {
381       // GWT's HashMap.entrySet().removeAll(null) doesn't throws NPE.
382     }
383   }
384 }
385