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