• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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 static com.google.common.collect.Maps.transformEntries;
20 import static com.google.common.collect.testing.testers.CollectionIteratorTester.getIteratorUnknownOrderRemoveSupportedMethod;
21 import static org.junit.contrib.truth.Truth.ASSERT;
22 
23 import com.google.common.annotations.GwtCompatible;
24 import com.google.common.annotations.GwtIncompatible;
25 import com.google.common.base.Equivalence;
26 import com.google.common.base.Equivalences;
27 import com.google.common.base.Function;
28 import com.google.common.base.Functions;
29 import com.google.common.base.Predicate;
30 import com.google.common.base.Predicates;
31 import com.google.common.collect.Maps.EntryTransformer;
32 import com.google.common.collect.Maps.ValueDifferenceImpl;
33 import com.google.common.collect.SetsTest.Derived;
34 import com.google.common.collect.testing.MapTestSuiteBuilder;
35 import com.google.common.collect.testing.SortedMapInterfaceTest;
36 import com.google.common.collect.testing.TestStringMapGenerator;
37 import com.google.common.collect.testing.features.CollectionSize;
38 import com.google.common.collect.testing.features.MapFeature;
39 import com.google.common.testing.EqualsTester;
40 import com.google.common.testing.NullPointerTester;
41 
42 import junit.framework.Test;
43 import junit.framework.TestCase;
44 import junit.framework.TestSuite;
45 
46 import java.io.IOException;
47 import java.lang.reflect.Field;
48 import java.util.Arrays;
49 import java.util.Collections;
50 import java.util.Comparator;
51 import java.util.EnumMap;
52 import java.util.Enumeration;
53 import java.util.HashMap;
54 import java.util.IdentityHashMap;
55 import java.util.Iterator;
56 import java.util.LinkedHashMap;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Map.Entry;
60 import java.util.Properties;
61 import java.util.Set;
62 import java.util.SortedMap;
63 import java.util.TreeMap;
64 import java.util.concurrent.ConcurrentMap;
65 
66 /**
67  * Unit test for {@code Maps}.
68  *
69  * @author Kevin Bourrillion
70  * @author Mike Bostock
71  * @author Jared Levy
72  */
73 @GwtCompatible(emulated = true)
74 public class MapsTest extends TestCase {
75 
76   private static final Comparator<Integer> SOME_COMPARATOR =
77       Collections.reverseOrder();
78 
testHashMap()79   public void testHashMap() {
80     HashMap<Integer, Integer> map = Maps.newHashMap();
81     assertEquals(Collections.emptyMap(), map);
82   }
83 
testHashMapWithInitialMap()84   public void testHashMapWithInitialMap() {
85     Map<String, Integer> original = new TreeMap<String, Integer>();
86     original.put("a", 1);
87     original.put("b", 2);
88     original.put("c", 3);
89     HashMap<String, Integer> map = Maps.newHashMap(original);
90     assertEquals(original, map);
91   }
92 
testHashMapGeneralizesTypes()93   public void testHashMapGeneralizesTypes() {
94     Map<String, Integer> original = new TreeMap<String, Integer>();
95     original.put("a", 1);
96     original.put("b", 2);
97     original.put("c", 3);
98     HashMap<Object, Object> map =
99         Maps.newHashMap((Map<? extends Object, ? extends Object>) original);
100     assertEquals(original, map);
101   }
102 
testCapacityForNegativeSizeFails()103   public void testCapacityForNegativeSizeFails() {
104     try {
105       Maps.capacity(-1);
106       fail("Negative expected size must result in IllegalArgumentException");
107     } catch (IllegalArgumentException ex) {
108     }
109   }
110 
111   /**
112    * Tests that nHMWES makes hash maps large enough that adding the expected
113    * number of elements won't cause a rehash.
114    *
115    * This test may fail miserably on non-OpenJDK environments...
116    */
117   @GwtIncompatible("reflection")
testNewHashMapWithExpectedSize_wontGrow()118   public void testNewHashMapWithExpectedSize_wontGrow() throws Exception {
119     for (int size = 0; size < 200; size++) {
120       HashMap<Integer, Void> map1 = Maps.newHashMapWithExpectedSize(size);
121 
122       int startSize = sizeOf(map1);
123 
124       for (int i = 0; i < size; i++) {
125         map1.put(i, null);
126       }
127       assertEquals("table size after adding " + size + "elements",
128           startSize, sizeOf(map1));
129 
130       /*
131        * Something slightly different happens when the entries are added all at
132        * once; make sure that passes too.
133        */
134       HashMap<Integer, Void> map2 = Maps.newHashMapWithExpectedSize(size);
135       map2.putAll(map1);
136       assertEquals("table size after adding " + size + "elements",
137           startSize, sizeOf(map2));
138     }
139   }
140 
141   @GwtIncompatible("reflection")
sizeOf(HashMap<?, ?> hashMap)142   private static int sizeOf(HashMap<?, ?> hashMap) throws Exception {
143     Field tableField = HashMap.class.getDeclaredField("table");
144     tableField.setAccessible(true);
145     Object[] table = (Object[]) tableField.get(hashMap);
146     return table.length;
147   }
148 
testCapacityForLargeSizes()149   public void testCapacityForLargeSizes() {
150     int[] largeExpectedSizes = new int[] {
151       Integer.MAX_VALUE / 2 - 1,
152       Integer.MAX_VALUE / 2,
153       Integer.MAX_VALUE / 2 + 1,
154       Integer.MAX_VALUE - 1,
155       Integer.MAX_VALUE};
156     for (int expectedSize : largeExpectedSizes) {
157       int capacity = Maps.capacity(expectedSize);
158       assertTrue(
159           "capacity (" + capacity + ") must be >= expectedSize (" + expectedSize + ")",
160           capacity >= expectedSize);
161     }
162   }
163 
testLinkedHashMap()164   public void testLinkedHashMap() {
165     LinkedHashMap<Integer, Integer> map = Maps.newLinkedHashMap();
166     assertEquals(Collections.emptyMap(), map);
167   }
168 
169   @SuppressWarnings("serial")
testLinkedHashMapWithInitialMap()170   public void testLinkedHashMapWithInitialMap() {
171     Map<String, String> map = new LinkedHashMap<String, String>() {{
172       put("Hello", "World");
173       put("first", "second");
174       put("polygene", "lubricants");
175       put("alpha", "betical");
176     }};
177 
178     LinkedHashMap<String, String> copy = Maps.newLinkedHashMap(map);
179 
180     Iterator<Entry<String, String>> iter = copy.entrySet().iterator();
181     assertTrue(iter.hasNext());
182     Entry<String, String> entry = iter.next();
183     assertEquals("Hello", entry.getKey());
184     assertEquals("World", entry.getValue());
185     assertTrue(iter.hasNext());
186 
187     entry = iter.next();
188     assertEquals("first", entry.getKey());
189     assertEquals("second", entry.getValue());
190     assertTrue(iter.hasNext());
191 
192     entry = iter.next();
193     assertEquals("polygene", entry.getKey());
194     assertEquals("lubricants", entry.getValue());
195     assertTrue(iter.hasNext());
196 
197     entry = iter.next();
198     assertEquals("alpha", entry.getKey());
199     assertEquals("betical", entry.getValue());
200     assertFalse(iter.hasNext());
201   }
202 
testLinkedHashMapGeneralizesTypes()203   public void testLinkedHashMapGeneralizesTypes() {
204     Map<String, Integer> original = new LinkedHashMap<String, Integer>();
205     original.put("a", 1);
206     original.put("b", 2);
207     original.put("c", 3);
208     HashMap<Object, Object> map
209         = Maps.<Object, Object>newLinkedHashMap(original);
210     assertEquals(original, map);
211   }
212 
testIdentityHashMap()213   public void testIdentityHashMap() {
214     IdentityHashMap<Integer, Integer> map = Maps.newIdentityHashMap();
215     assertEquals(Collections.emptyMap(), map);
216   }
217 
testConcurrentMap()218   public void testConcurrentMap() {
219     ConcurrentMap<Integer, Integer> map = Maps.newConcurrentMap();
220     assertEquals(Collections.emptyMap(), map);
221   }
222 
testTreeMap()223   public void testTreeMap() {
224     TreeMap<Integer, Integer> map = Maps.newTreeMap();
225     assertEquals(Collections.emptyMap(), map);
226     assertNull(map.comparator());
227   }
228 
testTreeMapDerived()229   public void testTreeMapDerived() {
230     TreeMap<Derived, Integer> map = Maps.newTreeMap();
231     assertEquals(Collections.emptyMap(), map);
232     map.put(new Derived("foo"), 1);
233     map.put(new Derived("bar"), 2);
234     ASSERT.that(map.keySet()).hasContentsInOrder(
235         new Derived("bar"), new Derived("foo"));
236     ASSERT.that(map.values()).hasContentsInOrder(2, 1);
237     assertNull(map.comparator());
238   }
239 
testTreeMapNonGeneric()240   public void testTreeMapNonGeneric() {
241     TreeMap<LegacyComparable, Integer> map = Maps.newTreeMap();
242     assertEquals(Collections.emptyMap(), map);
243     map.put(new LegacyComparable("foo"), 1);
244     map.put(new LegacyComparable("bar"), 2);
245     ASSERT.that(map.keySet()).hasContentsInOrder(
246         new LegacyComparable("bar"), new LegacyComparable("foo"));
247     ASSERT.that(map.values()).hasContentsInOrder(2, 1);
248     assertNull(map.comparator());
249   }
250 
testTreeMapWithComparator()251   public void testTreeMapWithComparator() {
252     TreeMap<Integer, Integer> map = Maps.newTreeMap(SOME_COMPARATOR);
253     assertEquals(Collections.emptyMap(), map);
254     assertSame(SOME_COMPARATOR, map.comparator());
255   }
256 
testTreeMapWithInitialMap()257   public void testTreeMapWithInitialMap() {
258     SortedMap<Integer, Integer> map = Maps.newTreeMap();
259     map.put(5, 10);
260     map.put(3, 20);
261     map.put(1, 30);
262     TreeMap<Integer, Integer> copy = Maps.newTreeMap(map);
263     assertEquals(copy, map);
264     assertSame(copy.comparator(), map.comparator());
265   }
266 
267   public enum SomeEnum { SOME_INSTANCE }
268 
testEnumMap()269   public void testEnumMap() {
270     EnumMap<SomeEnum, Integer> map = Maps.newEnumMap(SomeEnum.class);
271     assertEquals(Collections.emptyMap(), map);
272     map.put(SomeEnum.SOME_INSTANCE, 0);
273     assertEquals(Collections.singletonMap(SomeEnum.SOME_INSTANCE, 0), map);
274   }
275 
testEnumMapNullClass()276   public void testEnumMapNullClass() {
277     try {
278       Maps.<SomeEnum, Long>newEnumMap((Class<MapsTest.SomeEnum>) null);
279       fail("no exception thrown");
280     } catch (NullPointerException expected) {
281     }
282   }
283 
testEnumMapWithInitialEnumMap()284   public void testEnumMapWithInitialEnumMap() {
285     EnumMap<SomeEnum, Integer> original = Maps.newEnumMap(SomeEnum.class);
286     original.put(SomeEnum.SOME_INSTANCE, 0);
287     EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
288     assertEquals(original, copy);
289   }
290 
testEnumMapWithInitialEmptyEnumMap()291   public void testEnumMapWithInitialEmptyEnumMap() {
292     EnumMap<SomeEnum, Integer> original = Maps.newEnumMap(SomeEnum.class);
293     EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
294     assertEquals(original, copy);
295     assertNotSame(original, copy);
296   }
297 
testEnumMapWithInitialMap()298   public void testEnumMapWithInitialMap() {
299     HashMap<SomeEnum, Integer> original = Maps.newHashMap();
300     original.put(SomeEnum.SOME_INSTANCE, 0);
301     EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
302     assertEquals(original, copy);
303   }
304 
testEnumMapWithInitialEmptyMap()305   public void testEnumMapWithInitialEmptyMap() {
306     Map<SomeEnum, Integer> original = Maps.newHashMap();
307     try {
308       Maps.newEnumMap(original);
309       fail("Empty map must result in an IllegalArgumentException");
310     } catch (IllegalArgumentException expected) {}
311   }
312 
313   @GwtIncompatible("NullPointerTester")
testNullPointerExceptions()314   public void testNullPointerExceptions() throws Exception {
315     NullPointerTester tester = new NullPointerTester();
316     tester.setDefault(BiMap.class, ImmutableBiMap.of());
317     tester.setDefault(EntryTransformer.class, ALWAYS_NULL);
318     tester.setDefault(Equivalence.class, Equivalences.equals());
319     tester.setDefault(SortedMap.class, Maps.newTreeMap());
320     tester.ignore(Maps.class.getDeclaredMethod("uniqueIndex", Object.class, Function.class));
321     tester.testAllPublicStaticMethods(Maps.class);
322   }
323 
324   private static final EntryTransformer<Object, Object, Object> ALWAYS_NULL =
325       new EntryTransformer<Object, Object, Object>() {
326         @Override
327         public Object transformEntry(Object k, Object v1) {
328           return null;
329         }
330       };
331 
332   private static final Map<Integer, Integer> EMPTY
333       = Collections.emptyMap();
334   private static final Map<Integer, Integer> SINGLETON
335       = Collections.singletonMap(1, 2);
336 
testMapDifferenceEmptyEmpty()337   public void testMapDifferenceEmptyEmpty() {
338     MapDifference<Integer, Integer> diff = Maps.difference(EMPTY, EMPTY);
339     assertTrue(diff.areEqual());
340     assertEquals(EMPTY, diff.entriesOnlyOnLeft());
341     assertEquals(EMPTY, diff.entriesOnlyOnRight());
342     assertEquals(EMPTY, diff.entriesInCommon());
343     assertEquals(EMPTY, diff.entriesDiffering());
344     assertEquals("equal", diff.toString());
345   }
346 
testMapDifferenceEmptySingleton()347   public void testMapDifferenceEmptySingleton() {
348     MapDifference<Integer, Integer> diff = Maps.difference(EMPTY, SINGLETON);
349     assertFalse(diff.areEqual());
350     assertEquals(EMPTY, diff.entriesOnlyOnLeft());
351     assertEquals(SINGLETON, diff.entriesOnlyOnRight());
352     assertEquals(EMPTY, diff.entriesInCommon());
353     assertEquals(EMPTY, diff.entriesDiffering());
354     assertEquals("not equal: only on right={1=2}", diff.toString());
355   }
356 
testMapDifferenceSingletonEmpty()357   public void testMapDifferenceSingletonEmpty() {
358     MapDifference<Integer, Integer> diff = Maps.difference(SINGLETON, EMPTY);
359     assertFalse(diff.areEqual());
360     assertEquals(SINGLETON, diff.entriesOnlyOnLeft());
361     assertEquals(EMPTY, diff.entriesOnlyOnRight());
362     assertEquals(EMPTY, diff.entriesInCommon());
363     assertEquals(EMPTY, diff.entriesDiffering());
364     assertEquals("not equal: only on left={1=2}", diff.toString());
365   }
366 
testMapDifferenceTypical()367   public void testMapDifferenceTypical() {
368     Map<Integer, String> left = ImmutableMap.of(
369         1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
370     Map<Integer, String> right = ImmutableMap.of(
371         1, "a", 3, "f", 5, "g", 6, "z");
372 
373     MapDifference<Integer, String> diff1 = Maps.difference(left, right);
374     assertFalse(diff1.areEqual());
375     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff1.entriesOnlyOnLeft());
376     assertEquals(ImmutableMap.of(6, "z"), diff1.entriesOnlyOnRight());
377     assertEquals(ImmutableMap.of(1, "a"), diff1.entriesInCommon());
378     assertEquals(ImmutableMap.of(3,
379         ValueDifferenceImpl.create("c", "f"), 5,
380         ValueDifferenceImpl.create("e", "g")),
381         diff1.entriesDiffering());
382     assertEquals("not equal: only on left={2=b, 4=d}: only on right={6=z}: "
383         + "value differences={3=(c, f), 5=(e, g)}", diff1.toString());
384 
385     MapDifference<Integer, String> diff2 = Maps.difference(right, left);
386     assertFalse(diff2.areEqual());
387     assertEquals(ImmutableMap.of(6, "z"), diff2.entriesOnlyOnLeft());
388     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff2.entriesOnlyOnRight());
389     assertEquals(ImmutableMap.of(1, "a"), diff2.entriesInCommon());
390     assertEquals(ImmutableMap.of(3,
391         ValueDifferenceImpl.create("f", "c"), 5,
392         ValueDifferenceImpl.create("g", "e")),
393         diff2.entriesDiffering());
394     assertEquals("not equal: only on left={6=z}: only on right={2=b, 4=d}: "
395         + "value differences={3=(f, c), 5=(g, e)}", diff2.toString());
396   }
397 
testMapDifferenceEquals()398   public void testMapDifferenceEquals() {
399     Map<Integer, String> left = ImmutableMap.of(
400         1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
401     Map<Integer, String> right = ImmutableMap.of(
402         1, "a", 3, "f", 5, "g", 6, "z");
403     Map<Integer, String> right2 = ImmutableMap.of(
404         1, "a", 3, "h", 5, "g", 6, "z");
405     MapDifference<Integer, String> original = Maps.difference(left, right);
406     MapDifference<Integer, String> same = Maps.difference(left, right);
407     MapDifference<Integer, String> reverse = Maps.difference(right, left);
408     MapDifference<Integer, String> diff2 = Maps.difference(left, right2);
409 
410     new EqualsTester()
411         .addEqualityGroup(original, same)
412         .addEqualityGroup(reverse)
413         .addEqualityGroup(diff2)
414         .testEquals();
415   }
416 
testMapDifferencePredicateTypical()417   public void testMapDifferencePredicateTypical() {
418     Map<Integer, String> left = ImmutableMap.of(
419         1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
420     Map<Integer, String> right = ImmutableMap.of(
421         1, "A", 3, "F", 5, "G", 6, "Z");
422 
423     // TODO(kevinb): replace with Ascii.caseInsensitiveEquivalence() when it
424     // exists
425     Equivalence<String> caseInsensitiveEquivalence = Equivalences.equals().onResultOf(
426         new Function<String, String>() {
427           @Override public String apply(String input) {
428             return input.toLowerCase();
429           }
430         });
431 
432     MapDifference<Integer, String> diff1 = Maps.difference(left, right,
433         caseInsensitiveEquivalence);
434     assertFalse(diff1.areEqual());
435     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff1.entriesOnlyOnLeft());
436     assertEquals(ImmutableMap.of(6, "Z"), diff1.entriesOnlyOnRight());
437     assertEquals(ImmutableMap.of(1, "a"), diff1.entriesInCommon());
438     assertEquals(ImmutableMap.of(3,
439         ValueDifferenceImpl.create("c", "F"), 5,
440         ValueDifferenceImpl.create("e", "G")),
441         diff1.entriesDiffering());
442     assertEquals("not equal: only on left={2=b, 4=d}: only on right={6=Z}: "
443         + "value differences={3=(c, F), 5=(e, G)}", diff1.toString());
444 
445     MapDifference<Integer, String> diff2 = Maps.difference(right, left,
446         caseInsensitiveEquivalence);
447     assertFalse(diff2.areEqual());
448     assertEquals(ImmutableMap.of(6, "Z"), diff2.entriesOnlyOnLeft());
449     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff2.entriesOnlyOnRight());
450     assertEquals(ImmutableMap.of(1, "A"), diff2.entriesInCommon());
451     assertEquals(ImmutableMap.of(3,
452         ValueDifferenceImpl.create("F", "c"), 5,
453         ValueDifferenceImpl.create("G", "e")),
454         diff2.entriesDiffering());
455     assertEquals("not equal: only on left={6=Z}: only on right={2=b, 4=d}: "
456         + "value differences={3=(F, c), 5=(G, e)}", diff2.toString());
457   }
458 
459   private static final SortedMap<Integer, Integer> SORTED_EMPTY = Maps.newTreeMap();
460   private static final SortedMap<Integer, Integer> SORTED_SINGLETON =
461       ImmutableSortedMap.of(1, 2);
462 
testMapDifferenceOfSortedMapIsSorted()463   public void testMapDifferenceOfSortedMapIsSorted() {
464     Map<Integer, Integer> map = SORTED_SINGLETON;
465     MapDifference<Integer, Integer> difference = Maps.difference(map, EMPTY);
466     assertTrue(difference instanceof SortedMapDifference);
467   }
468 
testSortedMapDifferenceEmptyEmpty()469   public void testSortedMapDifferenceEmptyEmpty() {
470     SortedMapDifference<Integer, Integer> diff =
471         Maps.difference(SORTED_EMPTY, SORTED_EMPTY);
472     assertTrue(diff.areEqual());
473     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnLeft());
474     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnRight());
475     assertEquals(SORTED_EMPTY, diff.entriesInCommon());
476     assertEquals(SORTED_EMPTY, diff.entriesDiffering());
477     assertEquals("equal", diff.toString());
478   }
479 
testSortedMapDifferenceEmptySingleton()480   public void testSortedMapDifferenceEmptySingleton() {
481     SortedMapDifference<Integer, Integer> diff =
482         Maps.difference(SORTED_EMPTY, SORTED_SINGLETON);
483     assertFalse(diff.areEqual());
484     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnLeft());
485     assertEquals(SORTED_SINGLETON, diff.entriesOnlyOnRight());
486     assertEquals(SORTED_EMPTY, diff.entriesInCommon());
487     assertEquals(SORTED_EMPTY, diff.entriesDiffering());
488     assertEquals("not equal: only on right={1=2}", diff.toString());
489   }
490 
testSortedMapDifferenceSingletonEmpty()491   public void testSortedMapDifferenceSingletonEmpty() {
492     SortedMapDifference<Integer, Integer> diff =
493         Maps.difference(SORTED_SINGLETON, SORTED_EMPTY);
494     assertFalse(diff.areEqual());
495     assertEquals(SORTED_SINGLETON, diff.entriesOnlyOnLeft());
496     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnRight());
497     assertEquals(SORTED_EMPTY, diff.entriesInCommon());
498     assertEquals(SORTED_EMPTY, diff.entriesDiffering());
499     assertEquals("not equal: only on left={1=2}", diff.toString());
500   }
501 
testSortedMapDifferenceTypical()502   public void testSortedMapDifferenceTypical() {
503     SortedMap<Integer, String> left =
504         ImmutableSortedMap.<Integer, String>reverseOrder()
505         .put(1, "a").put(2, "b").put(3, "c").put(4, "d").put(5, "e")
506         .build();
507 
508     SortedMap<Integer, String> right =
509         ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z");
510 
511     SortedMapDifference<Integer, String> diff1 =
512         Maps.difference(left, right);
513     assertFalse(diff1.areEqual());
514     ASSERT.that(diff1.entriesOnlyOnLeft().entrySet()).hasContentsInOrder(
515         Maps.immutableEntry(4, "d"), Maps.immutableEntry(2, "b"));
516     ASSERT.that(diff1.entriesOnlyOnRight().entrySet()).hasContentsInOrder(
517         Maps.immutableEntry(6, "z"));
518     ASSERT.that(diff1.entriesInCommon().entrySet()).hasContentsInOrder(
519         Maps.immutableEntry(1, "a"));
520     ASSERT.that(diff1.entriesDiffering().entrySet()).hasContentsInOrder(
521         Maps.immutableEntry(5, ValueDifferenceImpl.create("e", "g")),
522         Maps.immutableEntry(3, ValueDifferenceImpl.create("c", "f")));
523     assertEquals("not equal: only on left={4=d, 2=b}: only on right={6=z}: "
524         + "value differences={5=(e, g), 3=(c, f)}", diff1.toString());
525 
526     SortedMapDifference<Integer, String> diff2 =
527         Maps.difference(right, left);
528     assertFalse(diff2.areEqual());
529     ASSERT.that(diff2.entriesOnlyOnLeft().entrySet()).hasContentsInOrder(
530         Maps.immutableEntry(6, "z"));
531     ASSERT.that(diff2.entriesOnlyOnRight().entrySet()).hasContentsInOrder(
532         Maps.immutableEntry(2, "b"), Maps.immutableEntry(4, "d"));
533     ASSERT.that(diff1.entriesInCommon().entrySet()).hasContentsInOrder(
534         Maps.immutableEntry(1, "a"));
535     assertEquals(ImmutableMap.of(
536             3, ValueDifferenceImpl.create("f", "c"),
537             5, ValueDifferenceImpl.create("g", "e")),
538         diff2.entriesDiffering());
539     assertEquals("not equal: only on left={6=z}: only on right={2=b, 4=d}: "
540         + "value differences={3=(f, c), 5=(g, e)}", diff2.toString());
541   }
542 
testSortedMapDifferenceImmutable()543   public void testSortedMapDifferenceImmutable() {
544     SortedMap<Integer, String> left = Maps.newTreeMap(
545         ImmutableSortedMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e"));
546     SortedMap<Integer, String> right =
547         Maps.newTreeMap(ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z"));
548 
549     SortedMapDifference<Integer, String> diff1 =
550         Maps.difference(left, right);
551     left.put(6, "z");
552     assertFalse(diff1.areEqual());
553     ASSERT.that(diff1.entriesOnlyOnLeft().entrySet()).hasContentsInOrder(
554         Maps.immutableEntry(2, "b"), Maps.immutableEntry(4, "d"));
555     ASSERT.that(diff1.entriesOnlyOnRight().entrySet()).hasContentsInOrder(
556         Maps.immutableEntry(6, "z"));
557     ASSERT.that(diff1.entriesInCommon().entrySet()).hasContentsInOrder(
558         Maps.immutableEntry(1, "a"));
559     ASSERT.that(diff1.entriesDiffering().entrySet()).hasContentsInOrder(
560         Maps.immutableEntry(3, ValueDifferenceImpl.create("c", "f")),
561         Maps.immutableEntry(5, ValueDifferenceImpl.create("e", "g")));
562     try {
563       diff1.entriesInCommon().put(7, "x");
564       fail();
565     } catch (UnsupportedOperationException expected) {
566     }
567     try {
568       diff1.entriesOnlyOnLeft().put(7, "x");
569       fail();
570     } catch (UnsupportedOperationException expected) {
571     }
572     try {
573       diff1.entriesOnlyOnRight().put(7, "x");
574       fail();
575     } catch (UnsupportedOperationException expected) {
576     }
577   }
578 
testSortedMapDifferenceEquals()579   public void testSortedMapDifferenceEquals() {
580     SortedMap<Integer, String> left =
581         ImmutableSortedMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
582     SortedMap<Integer, String> right =
583         ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z");
584     SortedMap<Integer, String> right2 =
585         ImmutableSortedMap.of(1, "a", 3, "h", 5, "g", 6, "z");
586     SortedMapDifference<Integer, String> original =
587         Maps.difference(left, right);
588     SortedMapDifference<Integer, String> same =
589         Maps.difference(left, right);
590     SortedMapDifference<Integer, String> reverse =
591         Maps.difference(right, left);
592     SortedMapDifference<Integer, String> diff2 =
593         Maps.difference(left, right2);
594 
595     new EqualsTester()
596         .addEqualityGroup(original, same)
597         .addEqualityGroup(reverse)
598         .addEqualityGroup(diff2)
599         .testEquals();
600   }
601 
602   private static final BiMap<Integer, String> INT_TO_STRING_MAP =
603       new ImmutableBiMap.Builder<Integer, String>()
604           .put(1, "one")
605           .put(2, "two")
606           .put(3, "three")
607           .build();
608 
testUniqueIndexCollection()609   public void testUniqueIndexCollection() {
610     ImmutableMap<Integer, String> outputMap =
611         Maps.uniqueIndex(INT_TO_STRING_MAP.values(),
612             Functions.forMap(INT_TO_STRING_MAP.inverse()));
613     assertEquals(INT_TO_STRING_MAP, outputMap);
614   }
615 
testUniqueIndexIterable()616   public void testUniqueIndexIterable() {
617     ImmutableMap<Integer, String> outputMap =
618         Maps.uniqueIndex(new Iterable<String>() {
619           @Override
620           public Iterator<String> iterator() {
621             return INT_TO_STRING_MAP.values().iterator();
622           }
623         },
624         Functions.forMap(INT_TO_STRING_MAP.inverse()));
625     assertEquals(INT_TO_STRING_MAP, outputMap);
626   }
627 
628   // NOTE: evil, never do this
629   private abstract static class IterableIterator<T>
630       extends ForwardingIterator<T> implements Iterable<T> {
631     @Override
iterator()632     public Iterator<T> iterator() {
633       return this;
634     }
635   }
636 
637   @SuppressWarnings("deprecation") // that is the purpose of this test
testUniqueIndexIterableIterator()638   public void testUniqueIndexIterableIterator() {
639     ImmutableMap<Integer, String> outputMap =
640         Maps.uniqueIndex(new IterableIterator<String>() {
641           private final Iterator<String> iterator = INT_TO_STRING_MAP.values().iterator();
642 
643           public Iterator<String> delegate() {
644             return iterator;
645           }
646         },
647         Functions.forMap(INT_TO_STRING_MAP.inverse()));
648     assertEquals(INT_TO_STRING_MAP, outputMap);
649   }
650 
testUniqueIndexIterator()651   public void testUniqueIndexIterator() {
652     ImmutableMap<Integer, String> outputMap =
653         Maps.uniqueIndex(INT_TO_STRING_MAP.values().iterator(),
654             Functions.forMap(INT_TO_STRING_MAP.inverse()));
655     assertEquals(INT_TO_STRING_MAP, outputMap);
656   }
657 
658   /** Can't create the map if more than one value maps to the same key. */
testUniqueIndexDuplicates()659   public void testUniqueIndexDuplicates() {
660     try {
661       Maps.uniqueIndex(ImmutableSet.of("one", "uno"), Functions.constant(1));
662       fail();
663     } catch (IllegalArgumentException expected) {
664     }
665   }
666 
667   /** Null values are not allowed. */
testUniqueIndexNullValue()668   public void testUniqueIndexNullValue() {
669     List<String> listWithNull = Lists.newArrayList((String) null);
670     try {
671       Maps.uniqueIndex(listWithNull, Functions.constant(1));
672       fail();
673     } catch (NullPointerException expected) {
674     }
675   }
676 
677   /** Null keys aren't allowed either. */
testUniqueIndexNullKey()678   public void testUniqueIndexNullKey() {
679     List<String> oneStringList = Lists.newArrayList("foo");
680     try {
681       Maps.uniqueIndex(oneStringList, Functions.constant(null));
682       fail();
683     } catch (NullPointerException expected) {
684     }
685   }
686 
687   @GwtIncompatible("Maps.fromProperties")
688   @SuppressWarnings("deprecation") // StringBufferInputStream
testFromProperties()689   public void testFromProperties() throws IOException {
690     Properties testProp = new Properties();
691 
692     Map<String, String> result = Maps.fromProperties(testProp);
693     assertTrue(result.isEmpty());
694     testProp.setProperty("first", "true");
695 
696     result = Maps.fromProperties(testProp);
697     assertEquals("true", result.get("first"));
698     assertEquals(1, result.size());
699     testProp.setProperty("second", "null");
700 
701     result = Maps.fromProperties(testProp);
702     assertEquals("true", result.get("first"));
703     assertEquals("null", result.get("second"));
704     assertEquals(2, result.size());
705 
706     // Now test values loaded from a stream.
707     String props = "test\n second = 2\n Third item :   a short  phrase   ";
708 
709     // TODO: change to StringReader in Java 1.6
710     testProp.load(new java.io.StringBufferInputStream(props));
711 
712     result = Maps.fromProperties(testProp);
713     assertEquals(4, result.size());
714     assertEquals("true", result.get("first"));
715     assertEquals("", result.get("test"));
716     assertEquals("2", result.get("second"));
717     assertEquals("item :   a short  phrase   ", result.get("Third"));
718     assertFalse(result.containsKey("not here"));
719 
720     // Test loading system properties
721     result = Maps.fromProperties(System.getProperties());
722     assertTrue(result.containsKey("java.version"));
723 
724     // Test that defaults work, too.
725     testProp = new Properties(System.getProperties());
726     String override = "test\njava.version : hidden";
727 
728     // TODO: change to StringReader in Java 1.6
729     testProp.load(new java.io.StringBufferInputStream(override));
730 
731     result = Maps.fromProperties(testProp);
732     assertTrue(result.size() > 2);
733     assertEquals("", result.get("test"));
734     assertEquals("hidden", result.get("java.version"));
735     assertNotSame(System.getProperty("java.version"),
736                   result.get("java.version"));
737   }
738 
739   @GwtIncompatible("Maps.fromProperties")
740   @SuppressWarnings("serial") // never serialized
testFromPropertiesNullKey()741   public void testFromPropertiesNullKey() {
742     Properties properties = new Properties() {
743       @Override public Enumeration<?> propertyNames() {
744         return Iterators.asEnumeration(
745             Arrays.asList(null, "first", "second").iterator());
746       }
747     };
748     properties.setProperty("first", "true");
749     properties.setProperty("second", "null");
750 
751     try {
752       Maps.fromProperties(properties);
753       fail();
754     } catch (NullPointerException expected) {}
755   }
756 
757   @GwtIncompatible("Maps.fromProperties")
758   @SuppressWarnings("serial") // never serialized
testFromPropertiesNonStringKeys()759   public void testFromPropertiesNonStringKeys() {
760     Properties properties = new Properties() {
761       @Override public Enumeration<?> propertyNames() {
762         return Iterators.asEnumeration(
763             Arrays.<Object>asList(Integer.valueOf(123), "first").iterator());
764       }
765     };
766 
767     try {
768       Maps.fromProperties(properties);
769       fail();
770     } catch (ClassCastException expected) {}
771   }
772 
773   /**
774    * Constructs a "nefarious" map entry with the specified key and value,
775    * meaning an entry that is suitable for testing that map entries cannot be
776    * modified via a nefarious implementation of equals. This is used for testing
777    * unmodifiable collections of map entries; for example, it should not be
778    * possible to access the raw (modifiable) map entry via a nefarious equals
779    * method.
780    */
nefariousEntry( final K key, final V value)781   public static <K, V> Map.Entry<K, V> nefariousEntry(
782       final K key, final V value) {
783     return new AbstractMapEntry<K, V>() {
784         @Override public K getKey() {
785           return key;
786         }
787         @Override public V getValue() {
788           return value;
789         }
790         @Override public V setValue(V value) {
791           throw new UnsupportedOperationException();
792         }
793         @SuppressWarnings("unchecked")
794         @Override public boolean equals(Object o) {
795           if (o instanceof Map.Entry<?, ?>) {
796             Map.Entry<K, V> e = (Map.Entry<K, V>) o;
797             e.setValue(value); // muhahaha!
798           }
799           return super.equals(o);
800         }
801       };
802   }
803 
804   public void testUnmodifiableBiMap() {
805     BiMap<Integer, String> mod = HashBiMap.create();
806     mod.put(1, "one");
807     mod.put(2, "two");
808     mod.put(3, "three");
809 
810     BiMap<Number, String> unmod = Maps.<Number, String>unmodifiableBiMap(mod);
811 
812     /* No aliasing on inverse operations. */
813     assertSame(unmod.inverse(), unmod.inverse());
814     assertSame(unmod, unmod.inverse().inverse());
815 
816     /* Unmodifiable is a view. */
817     mod.put(4, "four");
818     assertEquals(true, unmod.get(4).equals("four"));
819     assertEquals(true, unmod.inverse().get("four").equals(4));
820 
821     /* UnsupportedOperationException on direct modifications. */
822     try {
823       unmod.put(4, "four");
824       fail("UnsupportedOperationException expected");
825     } catch (UnsupportedOperationException expected) {}
826     try {
827       unmod.forcePut(4, "four");
828       fail("UnsupportedOperationException expected");
829     } catch (UnsupportedOperationException expected) {}
830     try {
831       unmod.putAll(Collections.singletonMap(4, "four"));
832       fail("UnsupportedOperationException expected");
833     } catch (UnsupportedOperationException expected) {}
834 
835     /* UnsupportedOperationException on indirect modifications. */
836     BiMap<String, Number> inverse = unmod.inverse();
837     try {
838       inverse.put("four", 4);
839       fail("UnsupportedOperationException expected");
840     } catch (UnsupportedOperationException expected) {}
841     try {
842       inverse.forcePut("four", 4);
843       fail("UnsupportedOperationException expected");
844     } catch (UnsupportedOperationException expected) {}
845     try {
846       inverse.putAll(Collections.singletonMap("four", 4));
847       fail("UnsupportedOperationException expected");
848     } catch (UnsupportedOperationException expected) {}
849     Set<String> values = unmod.values();
850     try {
851       values.remove("four");
852       fail("UnsupportedOperationException expected");
853     } catch (UnsupportedOperationException expected) {}
854     Set<Map.Entry<Number, String>> entries = unmod.entrySet();
855     Map.Entry<Number, String> entry = entries.iterator().next();
856     try {
857       entry.setValue("four");
858       fail("UnsupportedOperationException expected");
859     } catch (UnsupportedOperationException expected) {}
860     @SuppressWarnings("unchecked")
861     Map.Entry<Integer, String> entry2
862         = (Map.Entry<Integer, String>) entries.toArray()[0];
863     try {
864       entry2.setValue("four");
865       fail("UnsupportedOperationException expected");
866     } catch (UnsupportedOperationException expected) {}
867   }
868 
869   public void testBiMapEntrySetIteratorRemove() {
870     BiMap<Integer, String> map = HashBiMap.create();
871     map.put(1, "one");
872     Set<Map.Entry<Integer, String>> entries = map.entrySet();
873     Iterator<Map.Entry<Integer, String>> iterator = entries.iterator();
874     Map.Entry<Integer, String> entry = iterator.next();
875     entry.setValue("two"); // changes the iterator's current entry value
876     assertEquals("two", map.get(1));
877     iterator.remove(); // removes the updated entry
878     assertTrue(map.isEmpty());
879   }
880 
881   public void testImmutableEntry() {
882     Map.Entry<String, Integer> e = Maps.immutableEntry("foo", 1);
883     assertEquals("foo", e.getKey());
884     assertEquals(1, (int) e.getValue());
885     try {
886       e.setValue(2);
887       fail("UnsupportedOperationException expected");
888     } catch (UnsupportedOperationException expected) {}
889     assertEquals("foo=1", e.toString());
890     assertEquals(101575, e.hashCode());
891   }
892 
893   public void testImmutableEntryNull() {
894     Map.Entry<String, Integer> e
895         = Maps.immutableEntry((String) null, (Integer) null);
896     assertNull(e.getKey());
897     assertNull(e.getValue());
898     try {
899       e.setValue(null);
900       fail("UnsupportedOperationException expected");
901     } catch (UnsupportedOperationException expected) {}
902     assertEquals("null=null", e.toString());
903     assertEquals(0, e.hashCode());
904   }
905 
906   /** See {@link SynchronizedBiMapTest} for more tests. */
907   public void testSynchronizedBiMap() {
908     BiMap<String, Integer> bimap = HashBiMap.create();
909     bimap.put("one", 1);
910     BiMap<String, Integer> sync = Maps.synchronizedBiMap(bimap);
911     bimap.put("two", 2);
912     sync.put("three", 3);
913     assertEquals(ImmutableSet.of(1, 2, 3), bimap.inverse().keySet());
914     assertEquals(ImmutableSet.of(1, 2, 3), sync.inverse().keySet());
915   }
916 
917   private static final Predicate<String> NOT_LENGTH_3
918       = new Predicate<String>() {
919         @Override
920         public boolean apply(String input) {
921           return input == null || input.length() != 3;
922         }
923       };
924 
925   private static final Predicate<Integer> EVEN
926       = new Predicate<Integer>() {
927         @Override
928         public boolean apply(Integer input) {
929           return input == null || input % 2 == 0;
930         }
931       };
932 
933   private static final Predicate<Entry<String, Integer>> CORRECT_LENGTH
934       = new Predicate<Entry<String, Integer>>() {
935         @Override
936         public boolean apply(Entry<String, Integer> input) {
937           return input.getKey().length() == input.getValue();
938         }
939       };
940 
941   public void testFilteredKeysIllegalPut() {
942     Map<String, Integer> unfiltered = Maps.newHashMap();
943     Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
944     filtered.put("a", 1);
945     filtered.put("b", 2);
946     assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
947 
948     try {
949       filtered.put("yyy", 3);
950       fail();
951     } catch (IllegalArgumentException expected) {}
952 
953     try {
954       filtered.putAll(ImmutableMap.of("c", 3, "zzz", 4, "b", 5));
955       fail();
956     } catch (IllegalArgumentException expected) {}
957 
958     assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
959   }
960 
961   public void testFilteredKeysChangeFiltered() {
962     Map<String, Integer> unfiltered = Maps.newHashMap();
963     Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
964     unfiltered.put("two", 2);
965     unfiltered.put("three", 3);
966     unfiltered.put("four", 4);
967     assertEquals(ImmutableMap.of("two", 2, "three", 3, "four", 4), unfiltered);
968     assertEquals(ImmutableMap.of("three", 3, "four", 4), filtered);
969 
970     unfiltered.remove("three");
971     assertEquals(ImmutableMap.of("two", 2, "four", 4), unfiltered);
972     assertEquals(ImmutableMap.of("four", 4), filtered);
973 
974     unfiltered.clear();
975     assertEquals(ImmutableMap.of(), unfiltered);
976     assertEquals(ImmutableMap.of(), filtered);
977   }
978 
979   public void testFilteredKeysChangeUnfiltered() {
980     Map<String, Integer> unfiltered = Maps.newHashMap();
981     Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
982     unfiltered.put("two", 2);
983     unfiltered.put("three", 3);
984     unfiltered.put("four", 4);
985     assertEquals(ImmutableMap.of("two", 2, "three", 3, "four", 4), unfiltered);
986     assertEquals(ImmutableMap.of("three", 3, "four", 4), filtered);
987 
988     filtered.remove("three");
989     assertEquals(ImmutableMap.of("two", 2, "four", 4), unfiltered);
990     assertEquals(ImmutableMap.of("four", 4), filtered);
991 
992     filtered.clear();
993     assertEquals(ImmutableMap.of("two", 2), unfiltered);
994     assertEquals(ImmutableMap.of(), filtered);
995   }
996 
997   public void testFilteredValuesIllegalPut() {
998     Map<String, Integer> unfiltered = Maps.newHashMap();
999     Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1000     filtered.put("a", 2);
1001     unfiltered.put("b", 4);
1002     unfiltered.put("c", 5);
1003     assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1004 
1005     try {
1006       filtered.put("yyy", 3);
1007       fail();
1008     } catch (IllegalArgumentException expected) {}
1009 
1010     try {
1011       filtered.putAll(ImmutableMap.of("c", 4, "zzz", 5, "b", 6));
1012       fail();
1013     } catch (IllegalArgumentException expected) {}
1014 
1015     assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1016   }
1017 
1018   public void testFilteredValuesIllegalSetValue() {
1019     Map<String, Integer> unfiltered = Maps.newHashMap();
1020     Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1021     filtered.put("a", 2);
1022     filtered.put("b", 4);
1023     assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1024 
1025     Entry<String, Integer> entry = filtered.entrySet().iterator().next();
1026     try {
1027       entry.setValue(5);
1028       fail();
1029     } catch (IllegalArgumentException expected) {}
1030 
1031     assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1032   }
1033 
1034   public void testFilteredValuesClear() {
1035     Map<String, Integer> unfiltered = Maps.newHashMap();
1036     unfiltered.put("one", 1);
1037     unfiltered.put("two", 2);
1038     unfiltered.put("three", 3);
1039     unfiltered.put("four", 4);
1040     Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1041     assertEquals(ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4),
1042         unfiltered);
1043     assertEquals(ImmutableMap.of("two", 2, "four", 4), filtered);
1044 
1045     filtered.clear();
1046     assertEquals(ImmutableMap.of("one", 1, "three", 3), unfiltered);
1047     assertTrue(filtered.isEmpty());
1048   }
1049 
1050   public void testFilteredEntriesIllegalPut() {
1051     Map<String, Integer> unfiltered = Maps.newHashMap();
1052     unfiltered.put("cat", 3);
1053     unfiltered.put("dog", 2);
1054     unfiltered.put("horse", 5);
1055     Map<String, Integer> filtered
1056         = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1057     assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered);
1058 
1059     filtered.put("chicken", 7);
1060     assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1061 
1062     try {
1063       filtered.put("cow", 7);
1064       fail();
1065     } catch (IllegalArgumentException expected) {}
1066     assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1067 
1068     try {
1069       filtered.putAll(ImmutableMap.of("sheep", 5, "cow", 7));
1070       fail();
1071     } catch (IllegalArgumentException expected) {}
1072     assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1073   }
1074 
1075   public void testFilteredEntriesObjectPredicate() {
1076     Map<String, Integer> unfiltered = Maps.newHashMap();
1077     unfiltered.put("cat", 3);
1078     unfiltered.put("dog", 2);
1079     unfiltered.put("horse", 5);
1080     Predicate<Object> predicate = Predicates.alwaysFalse();
1081     Map<String, Integer> filtered
1082         = Maps.filterEntries(unfiltered, predicate);
1083     assertTrue(filtered.isEmpty());
1084   }
1085 
1086   public void testFilteredEntriesWildCardEntryPredicate() {
1087     Map<String, Integer> unfiltered = Maps.newHashMap();
1088     unfiltered.put("cat", 3);
1089     unfiltered.put("dog", 2);
1090     unfiltered.put("horse", 5);
1091     Predicate<Entry<?, ?>> predicate = new Predicate<Entry<?, ?>>() {
1092       @Override
1093       public boolean apply(Entry<?, ?> input) {
1094         return "cat".equals(input.getKey())
1095             || Integer.valueOf(2) == input.getValue();
1096       }
1097     };
1098     Map<String, Integer> filtered
1099         = Maps.filterEntries(unfiltered, predicate);
1100     assertEquals(ImmutableMap.of("cat", 3, "dog", 2), filtered);
1101   }
1102 
1103   public void testTransformValues() {
1104     Map<String, Integer> map = ImmutableMap.of("a", 4, "b", 9);
1105     Function<Integer, Double> sqrt = new Function<Integer, Double>() {
1106       @Override
1107       public Double apply(Integer in) {
1108         return Math.sqrt(in);
1109       }
1110     };
1111     Map<String, Double> transformed = Maps.transformValues(map, sqrt);
1112 
1113     assertEquals(ImmutableMap.of("a", 2.0, "b", 3.0), transformed);
1114   }
1115 
1116   public void testTransformValuesSecretlySorted() {
1117     Map<String, Integer> map = ImmutableSortedMap.of("a", 4, "b", 9);
1118     Function<Integer, Double> sqrt = new Function<Integer, Double>() {
1119       @Override
1120       public Double apply(Integer in) {
1121         return Math.sqrt(in);
1122       }
1123     };
1124     Map<String, Double> transformed = Maps.transformValues(map, sqrt);
1125 
1126     assertEquals(ImmutableMap.of("a", 2.0, "b", 3.0), transformed);
1127     assertTrue(transformed instanceof SortedMap);
1128   }
1129 
1130   public void testTransformEntries() {
1131     Map<String, String> map = ImmutableMap.of("a", "4", "b", "9");
1132     EntryTransformer<String, String, String> concat =
1133         new EntryTransformer<String, String, String>() {
1134           @Override
1135           public String transformEntry(String key, String value) {
1136             return key + value;
1137           }
1138         };
1139     Map<String, String> transformed = Maps.transformEntries(map, concat);
1140 
1141     assertEquals(ImmutableMap.of("a", "a4", "b", "b9"), transformed);
1142   }
1143 
1144   public void testTransformEntriesSecretlySorted() {
1145     Map<String, String> map = ImmutableSortedMap.of("a", "4", "b", "9");
1146     EntryTransformer<String, String, String> concat =
1147         new EntryTransformer<String, String, String>() {
1148           @Override
1149           public String transformEntry(String key, String value) {
1150             return key + value;
1151           }
1152         };
1153     Map<String, String> transformed = Maps.transformEntries(map, concat);
1154 
1155     assertEquals(ImmutableMap.of("a", "a4", "b", "b9"), transformed);
1156     assertTrue(transformed instanceof SortedMap);
1157   }
1158 
1159   public void testTransformEntriesGenerics() {
1160     Map<Object, Object> map1 = ImmutableMap.<Object, Object>of(1, 2);
1161     Map<Object, Number> map2 = ImmutableMap.<Object, Number>of(1, 2);
1162     Map<Object, Integer> map3 = ImmutableMap.<Object, Integer>of(1, 2);
1163     Map<Number, Object> map4 = ImmutableMap.<Number, Object>of(1, 2);
1164     Map<Number, Number> map5 = ImmutableMap.<Number, Number>of(1, 2);
1165     Map<Number, Integer> map6 = ImmutableMap.<Number, Integer>of(1, 2);
1166     Map<Integer, Object> map7 = ImmutableMap.<Integer, Object>of(1, 2);
1167     Map<Integer, Number> map8 = ImmutableMap.<Integer, Number>of(1, 2);
1168     Map<Integer, Integer> map9 = ImmutableMap.<Integer, Integer>of(1, 2);
1169     Map<? extends Number, ? extends Number> map0 = ImmutableMap.of(1, 2);
1170 
1171     EntryTransformer<Number, Number, Double> transformer =
1172         new EntryTransformer<Number, Number, Double>() {
1173           @Override
1174           public Double transformEntry(Number key, Number value) {
1175             return key.doubleValue() + value.doubleValue();
1176           }
1177         };
1178 
1179     Map<Object, Double> objectKeyed;
1180     Map<Number, Double> numberKeyed;
1181     Map<Integer, Double> integerKeyed;
1182 
1183     numberKeyed = transformEntries(map5, transformer);
1184     numberKeyed = transformEntries(map6, transformer);
1185     integerKeyed = transformEntries(map8, transformer);
1186     integerKeyed = transformEntries(map9, transformer);
1187 
1188     Map<? extends Number, Double> wildcarded = transformEntries(map0, transformer);
1189 
1190     // Can't loosen the key type:
1191     // objectKeyed = transformEntries(map5, transformer);
1192     // objectKeyed = transformEntries(map6, transformer);
1193     // objectKeyed = transformEntries(map8, transformer);
1194     // objectKeyed = transformEntries(map9, transformer);
1195     // numberKeyed = transformEntries(map8, transformer);
1196     // numberKeyed = transformEntries(map9, transformer);
1197 
1198     // Can't loosen the value type:
1199     // Map<Number, Number> looseValued1 = transformEntries(map5, transformer);
1200     // Map<Number, Number> looseValued2 = transformEntries(map6, transformer);
1201     // Map<Integer, Number> looseValued3 = transformEntries(map8, transformer);
1202     // Map<Integer, Number> looseValued4 = transformEntries(map9, transformer);
1203 
1204     // Can't call with too loose a key:
1205     // transformEntries(map1, transformer);
1206     // transformEntries(map2, transformer);
1207     // transformEntries(map3, transformer);
1208 
1209     // Can't call with too loose a value:
1210     // transformEntries(map1, transformer);
1211     // transformEntries(map4, transformer);
1212     // transformEntries(map7, transformer);
1213   }
1214 
1215   public void testTransformEntriesExample() {
1216     Map<String, Boolean> options =
1217         ImmutableMap.of("verbose", true, "sort", false);
1218     EntryTransformer<String, Boolean, String> flagPrefixer =
1219         new EntryTransformer<String, Boolean, String>() {
1220           @Override
1221           public String transformEntry(String key, Boolean value) {
1222             return value ? key : "no" + key;
1223           }
1224         };
1225     Map<String, String> transformed =
1226         Maps.transformEntries(options, flagPrefixer);
1227     assertEquals("{verbose=verbose, sort=nosort}", transformed.toString());
1228   }
1229 
1230   // TestStringMapGenerator uses entries of the form "one=January" and so forth.
1231   // To test the filtered collections, we'll create a map containing the entries
1232   // they ask for, plus some bogus numeric entries. Then our predicates will
1233   // simply filter numeric entries back out.
1234 
1235   private static ImmutableMap<String, String> ENTRIES_TO_FILTER_OUT =
1236       new ImmutableMap.Builder<String, String>()
1237           .put("0", "0")
1238           .put("1", "1")
1239           .put("2", "2")
1240           .build();
1241 
1242   @GwtIncompatible("suite")
1243   public static class FilteredMapTests extends TestCase {
1244     public static Test suite() {
1245       TestSuite suite = new TestSuite();
1246 
1247       suite.addTest(MapTestSuiteBuilder.using(
1248           new TestStringMapGenerator() {
1249             @Override protected Map<String, String> create(
1250                 Entry<String, String>[] entries) {
1251               Map<String, String> map = Maps.newHashMap();
1252               for (Entry<String, String> entry : entries) {
1253                 map.put(entry.getKey(), entry.getValue());
1254               }
1255               map.putAll(ENTRIES_TO_FILTER_OUT);
1256               return Maps.filterKeys(map, new Predicate<String>() {
1257                 @Override
1258                 public boolean apply(String input) {
1259                   return input == null
1260                       || (input.charAt(0) >= 'a' && input.charAt(0) <= 'z');
1261                 }
1262               });
1263             }
1264           })
1265           .named("Maps.filterKeys")
1266           .withFeatures(
1267               CollectionSize.ANY,
1268               MapFeature.ALLOWS_NULL_KEYS,
1269               MapFeature.ALLOWS_NULL_VALUES,
1270               MapFeature.GENERAL_PURPOSE)
1271           .suppressing(getIteratorUnknownOrderRemoveSupportedMethod())
1272           .createTestSuite());
1273 
1274       suite.addTest(MapTestSuiteBuilder.using(
1275           new TestStringMapGenerator() {
1276             @Override protected Map<String, String> create(
1277                 Entry<String, String>[] entries) {
1278               Map<String, String> map = Maps.newHashMap();
1279               for (Entry<String, String> entry : entries) {
1280                 map.put(entry.getKey(), entry.getValue());
1281               }
1282               map.putAll(ENTRIES_TO_FILTER_OUT);
1283               return Maps.filterValues(map, new Predicate<String>() {
1284                 @Override
1285                 public boolean apply(String input) {
1286                   return input == null
1287                       || (input.charAt(0) >= 'A' && input.charAt(0) <= 'Z');
1288                 }
1289               });
1290             }
1291           })
1292           .named("Maps.filterValues")
1293           .withFeatures(
1294               CollectionSize.ANY,
1295               MapFeature.ALLOWS_NULL_KEYS,
1296               MapFeature.ALLOWS_NULL_VALUES,
1297               MapFeature.GENERAL_PURPOSE)
1298           .suppressing(getIteratorUnknownOrderRemoveSupportedMethod())
1299           .createTestSuite());
1300 
1301       suite.addTest(MapTestSuiteBuilder.using(
1302           new TestStringMapGenerator() {
1303             @Override protected Map<String, String> create(
1304                 Entry<String, String>[] entries) {
1305               Map<String, String> map = Maps.newHashMap();
1306               for (Entry<String, String> entry : entries) {
1307                 map.put(entry.getKey(), entry.getValue());
1308               }
1309               map.putAll(ENTRIES_TO_FILTER_OUT);
1310               return Maps.filterEntries(map,
1311                   new Predicate<Entry<String, String>>() {
1312                     @Override
1313                     public boolean apply(Entry<String, String> entry) {
1314                       String input = entry.getKey();
1315                       return input == null
1316                           || (input.charAt(0) >= 'a' && input.charAt(0) <= 'z');
1317                     }
1318                   });
1319             }
1320           })
1321           .named("Maps.filterEntries")
1322           .withFeatures(
1323               CollectionSize.ANY,
1324               MapFeature.ALLOWS_NULL_KEYS,
1325               MapFeature.ALLOWS_NULL_VALUES,
1326               MapFeature.GENERAL_PURPOSE)
1327           .suppressing(getIteratorUnknownOrderRemoveSupportedMethod())
1328           .createTestSuite());
1329 
1330       suite.addTest(MapTestSuiteBuilder.using(
1331           new TestStringMapGenerator() {
1332             @Override protected Map<String, String> create(
1333                 Entry<String, String>[] entries) {
1334               Map<String, String> map = Maps.newHashMap();
1335               for (Entry<String, String> entry : entries) {
1336                 map.put(entry.getKey(), entry.getValue());
1337               }
1338               map.putAll(ENTRIES_TO_FILTER_OUT);
1339               map.put("", "weird");
1340               Map<String, String> withoutEmptyKey = Maps.filterKeys(map,
1341                   new Predicate<String>() {
1342                     @Override
1343                     public boolean apply(String input) {
1344                       return input == null || input.length() != 0;
1345                     }
1346                   });
1347               return Maps.filterKeys(withoutEmptyKey, new Predicate<String>() {
1348                 @Override
1349                 public boolean apply(String input) {
1350                   return input == null
1351                       || (input.charAt(0) >= 'a' && input.charAt(0) <= 'z');
1352                 }
1353               });
1354               // note: these filters were deliberately chosen so that an
1355               // element somehow getting around the first filter would cause
1356               // an exception in the second
1357             }
1358           })
1359           .named("Maps.filterKeys, chained")
1360           .withFeatures(
1361               CollectionSize.ANY,
1362               MapFeature.ALLOWS_NULL_KEYS,
1363               MapFeature.ALLOWS_NULL_VALUES,
1364               MapFeature.GENERAL_PURPOSE)
1365           .suppressing(getIteratorUnknownOrderRemoveSupportedMethod())
1366           .createTestSuite());
1367 
1368       return suite;
1369     }
1370   }
1371 
1372   public void testSortedMapTransformValues() {
1373     SortedMap<String, Integer> map = ImmutableSortedMap.of("a", 4, "b", 9);
1374     Function<Integer, Double> sqrt = new Function<Integer, Double>() {
1375       @Override
1376       public Double apply(Integer in) {
1377         return Math.sqrt(in);
1378       }
1379     };
1380     SortedMap<String, Double> transformed =
1381         Maps.transformValues(map, sqrt);
1382 
1383     assertEquals(ImmutableSortedMap.of("a", 2.0, "b", 3.0), transformed);
1384   }
1385 
1386   public void testSortedMapTransformEntries() {
1387     SortedMap<String, String> map = ImmutableSortedMap.of("a", "4", "b", "9");
1388     EntryTransformer<String, String, String> concat =
1389         new EntryTransformer<String, String, String>() {
1390           @Override
1391           public String transformEntry(String key, String value) {
1392             return key + value;
1393           }
1394         };
1395     SortedMap<String, String> transformed =
1396         Maps.transformEntries(map, concat);
1397 
1398     assertEquals(ImmutableSortedMap.of("a", "a4", "b", "b9"), transformed);
1399   }
1400 
1401   /*
1402    * Not testing Map methods of Maps.filter*(SortedMap), since the
1403    * implementation doesn't override Maps.FilteredEntryMap, which is already
1404    * tested.
1405    */
1406 
1407   public void testSortedMapFilterKeys() {
1408     Comparator<Integer> comparator = Ordering.natural();
1409     SortedMap<Integer, String> unfiltered = Maps.newTreeMap(comparator);
1410     unfiltered.put(1, "one");
1411     unfiltered.put(2, "two");
1412     unfiltered.put(3, "three");
1413     unfiltered.put(4, "four");
1414     unfiltered.put(5, "five");
1415     unfiltered.put(6, "six");
1416     unfiltered.put(7, "seven");
1417     SortedMap<Integer, String> filtered
1418         = Maps.filterKeys(unfiltered, EVEN);
1419     ASSERT.that(filtered.keySet()).hasContentsInOrder(2, 4, 6);
1420     assertSame(comparator, filtered.comparator());
1421     assertEquals((Integer) 2, filtered.firstKey());
1422     assertEquals((Integer) 6, filtered.lastKey());
1423     ASSERT.that(filtered.headMap(5).keySet()).hasContentsInOrder(2, 4);
1424     ASSERT.that(filtered.tailMap(3).keySet()).hasContentsInOrder(4, 6);
1425     ASSERT.that(filtered.subMap(3, 5).keySet()).hasContentsInOrder(4);
1426   }
1427 
1428   public void testSortedMapFilterValues() {
1429     Comparator<Integer> comparator = Ordering.natural();
1430     SortedMap<Integer, String> unfiltered = Maps.newTreeMap(comparator);
1431     unfiltered.put(1, "one");
1432     unfiltered.put(2, "two");
1433     unfiltered.put(3, "three");
1434     unfiltered.put(4, "four");
1435     unfiltered.put(5, "five");
1436     unfiltered.put(6, "six");
1437     unfiltered.put(7, "seven");
1438     SortedMap<Integer, String> filtered
1439         = Maps.filterValues(unfiltered, NOT_LENGTH_3);
1440     ASSERT.that(filtered.keySet()).hasContentsInOrder(3, 4, 5, 7);
1441     assertSame(comparator, filtered.comparator());
1442     assertEquals((Integer) 3, filtered.firstKey());
1443     assertEquals((Integer) 7, filtered.lastKey());
1444     ASSERT.that(filtered.headMap(5).keySet()).hasContentsInOrder(3, 4);
1445     ASSERT.that(filtered.tailMap(4).keySet()).hasContentsInOrder(4, 5, 7);
1446     ASSERT.that(filtered.subMap(4, 6).keySet()).hasContentsInOrder(4, 5);
1447   }
1448 
1449   private static final Predicate<Map.Entry<Integer, String>>
1450       EVEN_AND_LENGTH_3 = new Predicate<Map.Entry<Integer, String>>() {
1451         @Override public boolean apply(Entry<Integer, String> entry) {
1452           return (entry.getKey() == null || entry.getKey() % 2 == 0)
1453               && (entry.getValue() == null || entry.getValue().length() == 3);
1454         }
1455     };
1456 
1457   private static class ContainsKeySafeSortedMap
1458       extends ForwardingSortedMap<Integer, String> {
1459     SortedMap<Integer, String> delegate
1460         = Maps.newTreeMap(Ordering.natural().nullsFirst());
1461 
1462     @Override protected SortedMap<Integer, String> delegate() {
1463       return delegate;
1464     }
1465 
1466     // Needed by MapInterfaceTest.testContainsKey()
1467     @Override public boolean containsKey(Object key) {
1468       try {
1469         return super.containsKey(key);
1470       } catch (ClassCastException e) {
1471         return false;
1472       }
1473     }
1474   }
1475 
1476   public static class FilteredEntriesSortedMapInterfaceTest
1477       extends SortedMapInterfaceTest<Integer, String> {
1478     public FilteredEntriesSortedMapInterfaceTest() {
1479       super(true, true, true, true, true);
1480     }
1481 
1482     @Override protected SortedMap<Integer, String> makeEmptyMap() {
1483       SortedMap<Integer, String> unfiltered = new ContainsKeySafeSortedMap();
1484       unfiltered.put(1, "one");
1485       unfiltered.put(3, "three");
1486       unfiltered.put(4, "four");
1487       unfiltered.put(5, "five");
1488       return Maps.filterEntries(unfiltered, EVEN_AND_LENGTH_3);
1489     }
1490 
1491     @Override protected SortedMap<Integer, String> makePopulatedMap() {
1492       SortedMap<Integer, String> unfiltered = new ContainsKeySafeSortedMap();
1493       unfiltered.put(1, "one");
1494       unfiltered.put(2, "two");
1495       unfiltered.put(3, "three");
1496       unfiltered.put(4, "four");
1497       unfiltered.put(5, "five");
1498       unfiltered.put(6, "six");
1499       return Maps.filterEntries(unfiltered, EVEN_AND_LENGTH_3);
1500     }
1501 
1502     @Override protected Integer getKeyNotInPopulatedMap() {
1503       return 10;
1504     }
1505 
1506     @Override protected String getValueNotInPopulatedMap() {
1507       return "ten";
1508     }
1509 
1510     // Iterators don't support remove.
1511     @Override public void testEntrySetIteratorRemove() {}
1512     @Override public void testValuesIteratorRemove() {}
1513 
1514     // These tests fail on GWT.
1515     // TODO: Investigate why.
1516     @Override public void testEntrySetRemoveAll() {}
1517     @Override public void testEntrySetRetainAll() {}
1518   }
1519 }
1520