View Javadoc
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.Maps.transformValues;
21  import static com.google.common.collect.Maps.unmodifiableNavigableMap;
22  import static com.google.common.collect.testing.Helpers.mapEntry;
23  import static com.google.common.truth.Truth.assertThat;
24  import static java.util.Arrays.asList;
25  
26  import com.google.common.annotations.GwtCompatible;
27  import com.google.common.annotations.GwtIncompatible;
28  import com.google.common.base.Converter;
29  import com.google.common.base.Equivalence;
30  import com.google.common.base.Function;
31  import com.google.common.base.Functions;
32  import com.google.common.base.Predicate;
33  import com.google.common.base.Predicates;
34  import com.google.common.collect.Maps.EntryTransformer;
35  import com.google.common.collect.Maps.ValueDifferenceImpl;
36  import com.google.common.collect.SetsTest.Derived;
37  import com.google.common.testing.EqualsTester;
38  import com.google.common.testing.NullPointerTester;
39  import com.google.common.testing.SerializableTester;
40  
41  import junit.framework.TestCase;
42  
43  import java.io.IOException;
44  import java.io.StringReader;
45  import java.lang.reflect.Field;
46  import java.util.Arrays;
47  import java.util.Collection;
48  import java.util.Collections;
49  import java.util.Comparator;
50  import java.util.EnumMap;
51  import java.util.Enumeration;
52  import java.util.HashMap;
53  import java.util.IdentityHashMap;
54  import java.util.Iterator;
55  import java.util.LinkedHashMap;
56  import java.util.List;
57  import java.util.Map;
58  import java.util.Map.Entry;
59  import java.util.NavigableMap;
60  import java.util.NavigableSet;
61  import java.util.Properties;
62  import java.util.Set;
63  import java.util.SortedMap;
64  import java.util.SortedSet;
65  import java.util.TreeMap;
66  import java.util.concurrent.ConcurrentMap;
67  
68  /**
69   * Unit test for {@code Maps}.
70   *
71   * @author Kevin Bourrillion
72   * @author Mike Bostock
73   * @author Jared Levy
74   */
75  @GwtCompatible(emulated = true)
76  public class MapsTest extends TestCase {
77  
78    private static final Comparator<Integer> SOME_COMPARATOR =
79        Collections.reverseOrder();
80  
81    public void testHashMap() {
82      HashMap<Integer, Integer> map = Maps.newHashMap();
83      assertEquals(Collections.emptyMap(), map);
84    }
85  
86    public void testHashMapWithInitialMap() {
87      Map<String, Integer> original = new TreeMap<String, Integer>();
88      original.put("a", 1);
89      original.put("b", 2);
90      original.put("c", 3);
91      HashMap<String, Integer> map = Maps.newHashMap(original);
92      assertEquals(original, map);
93    }
94  
95    public void testHashMapGeneralizesTypes() {
96      Map<String, Integer> original = new TreeMap<String, Integer>();
97      original.put("a", 1);
98      original.put("b", 2);
99      original.put("c", 3);
100     HashMap<Object, Object> map =
101         Maps.newHashMap((Map<? extends Object, ? extends Object>) original);
102     assertEquals(original, map);
103   }
104 
105   public void testCapacityForNegativeSizeFails() {
106     try {
107       Maps.capacity(-1);
108       fail("Negative expected size must result in IllegalArgumentException");
109     } catch (IllegalArgumentException ex) {
110     }
111   }
112 
113   /**
114    * Tests that nHMWES makes hash maps large enough that adding the expected
115    * number of elements won't cause a rehash.
116    *
117    * As of jdk7u40, HashMap has an empty-map optimization.  The argument to
118    * new HashMap(int) is noted, but the initial table is a zero-length array.
119    *
120    * This test may fail miserably on non-OpenJDK environments...
121    */
122   @GwtIncompatible("reflection")
123   public void testNewHashMapWithExpectedSize_wontGrow() throws Exception {
124     // before jdk7u40: creates one-bucket table
125     // after  jdk7u40: creates empty table
126     assertTrue(bucketsOf(Maps.newHashMapWithExpectedSize(0)) <= 1);
127 
128     for (int size = 1; size < 200; size++) {
129       HashMap<Integer, Void> map1 = Maps.newHashMapWithExpectedSize(size);
130 
131       // Only start measuring table size after the first element inserted, to
132       // deal with empty-map optimization.
133       map1.put(0, null);
134 
135       int initialBuckets = bucketsOf(map1);
136 
137       for (int i = 1; i < size; i++) {
138         map1.put(i, null);
139       }
140       assertEquals("table size after adding " + size + " elements",
141           initialBuckets, bucketsOf(map1));
142 
143       /*
144        * Something slightly different happens when the entries are added all at
145        * once; make sure that passes too.
146        */
147       HashMap<Integer, Void> map2 = Maps.newHashMapWithExpectedSize(size);
148       map2.putAll(map1);
149       assertEquals("table size after adding " + size + "elements",
150           initialBuckets, bucketsOf(map2));
151     }
152   }
153 
154   @GwtIncompatible("reflection")
155   private static int bucketsOf(HashMap<?, ?> hashMap) throws Exception {
156     Field tableField = HashMap.class.getDeclaredField("table");
157     tableField.setAccessible(true);
158     Object[] table = (Object[]) tableField.get(hashMap);
159     return table.length;
160   }
161 
162   public void testCapacityForLargeSizes() {
163     int[] largeExpectedSizes = new int[] {
164       Integer.MAX_VALUE / 2 - 1,
165       Integer.MAX_VALUE / 2,
166       Integer.MAX_VALUE / 2 + 1,
167       Integer.MAX_VALUE - 1,
168       Integer.MAX_VALUE};
169     for (int expectedSize : largeExpectedSizes) {
170       int capacity = Maps.capacity(expectedSize);
171       assertTrue(
172           "capacity (" + capacity + ") must be >= expectedSize (" + expectedSize + ")",
173           capacity >= expectedSize);
174     }
175   }
176 
177   public void testLinkedHashMap() {
178     LinkedHashMap<Integer, Integer> map = Maps.newLinkedHashMap();
179     assertEquals(Collections.emptyMap(), map);
180   }
181 
182   @SuppressWarnings("serial")
183   public void testLinkedHashMapWithInitialMap() {
184     Map<String, String> map = new LinkedHashMap<String, String>() {{
185       put("Hello", "World");
186       put("first", "second");
187       put("polygene", "lubricants");
188       put("alpha", "betical");
189     }};
190 
191     LinkedHashMap<String, String> copy = Maps.newLinkedHashMap(map);
192 
193     Iterator<Entry<String, String>> iter = copy.entrySet().iterator();
194     assertTrue(iter.hasNext());
195     Entry<String, String> entry = iter.next();
196     assertEquals("Hello", entry.getKey());
197     assertEquals("World", entry.getValue());
198     assertTrue(iter.hasNext());
199 
200     entry = iter.next();
201     assertEquals("first", entry.getKey());
202     assertEquals("second", entry.getValue());
203     assertTrue(iter.hasNext());
204 
205     entry = iter.next();
206     assertEquals("polygene", entry.getKey());
207     assertEquals("lubricants", entry.getValue());
208     assertTrue(iter.hasNext());
209 
210     entry = iter.next();
211     assertEquals("alpha", entry.getKey());
212     assertEquals("betical", entry.getValue());
213     assertFalse(iter.hasNext());
214   }
215 
216   public void testLinkedHashMapGeneralizesTypes() {
217     Map<String, Integer> original = new LinkedHashMap<String, Integer>();
218     original.put("a", 1);
219     original.put("b", 2);
220     original.put("c", 3);
221     HashMap<Object, Object> map
222         = Maps.<Object, Object>newLinkedHashMap(original);
223     assertEquals(original, map);
224   }
225 
226   public void testIdentityHashMap() {
227     IdentityHashMap<Integer, Integer> map = Maps.newIdentityHashMap();
228     assertEquals(Collections.emptyMap(), map);
229   }
230 
231   public void testConcurrentMap() {
232     ConcurrentMap<Integer, Integer> map = Maps.newConcurrentMap();
233     assertEquals(Collections.emptyMap(), map);
234   }
235 
236   public void testTreeMap() {
237     TreeMap<Integer, Integer> map = Maps.newTreeMap();
238     assertEquals(Collections.emptyMap(), map);
239     assertNull(map.comparator());
240   }
241 
242   public void testTreeMapDerived() {
243     TreeMap<Derived, Integer> map = Maps.newTreeMap();
244     assertEquals(Collections.emptyMap(), map);
245     map.put(new Derived("foo"), 1);
246     map.put(new Derived("bar"), 2);
247     assertThat(map.keySet()).has().exactly(
248         new Derived("bar"), new Derived("foo")).inOrder();
249     assertThat(map.values()).has().exactly(2, 1).inOrder();
250     assertNull(map.comparator());
251   }
252 
253   public void testTreeMapNonGeneric() {
254     TreeMap<LegacyComparable, Integer> map = Maps.newTreeMap();
255     assertEquals(Collections.emptyMap(), map);
256     map.put(new LegacyComparable("foo"), 1);
257     map.put(new LegacyComparable("bar"), 2);
258     assertThat(map.keySet()).has().exactly(
259         new LegacyComparable("bar"), new LegacyComparable("foo")).inOrder();
260     assertThat(map.values()).has().exactly(2, 1).inOrder();
261     assertNull(map.comparator());
262   }
263 
264   public void testTreeMapWithComparator() {
265     TreeMap<Integer, Integer> map = Maps.newTreeMap(SOME_COMPARATOR);
266     assertEquals(Collections.emptyMap(), map);
267     assertSame(SOME_COMPARATOR, map.comparator());
268   }
269 
270   public void testTreeMapWithInitialMap() {
271     SortedMap<Integer, Integer> map = Maps.newTreeMap();
272     map.put(5, 10);
273     map.put(3, 20);
274     map.put(1, 30);
275     TreeMap<Integer, Integer> copy = Maps.newTreeMap(map);
276     assertEquals(copy, map);
277     assertSame(copy.comparator(), map.comparator());
278   }
279 
280   public enum SomeEnum { SOME_INSTANCE }
281 
282   public void testEnumMap() {
283     EnumMap<SomeEnum, Integer> map = Maps.newEnumMap(SomeEnum.class);
284     assertEquals(Collections.emptyMap(), map);
285     map.put(SomeEnum.SOME_INSTANCE, 0);
286     assertEquals(Collections.singletonMap(SomeEnum.SOME_INSTANCE, 0), map);
287   }
288 
289   public void testEnumMapNullClass() {
290     try {
291       Maps.<SomeEnum, Long>newEnumMap((Class<MapsTest.SomeEnum>) null);
292       fail("no exception thrown");
293     } catch (NullPointerException expected) {
294     }
295   }
296 
297   public void testEnumMapWithInitialEnumMap() {
298     EnumMap<SomeEnum, Integer> original = Maps.newEnumMap(SomeEnum.class);
299     original.put(SomeEnum.SOME_INSTANCE, 0);
300     EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
301     assertEquals(original, copy);
302   }
303 
304   public void testEnumMapWithInitialEmptyEnumMap() {
305     EnumMap<SomeEnum, Integer> original = Maps.newEnumMap(SomeEnum.class);
306     EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
307     assertEquals(original, copy);
308     assertNotSame(original, copy);
309   }
310 
311   public void testEnumMapWithInitialMap() {
312     HashMap<SomeEnum, Integer> original = Maps.newHashMap();
313     original.put(SomeEnum.SOME_INSTANCE, 0);
314     EnumMap<SomeEnum, Integer> copy = Maps.newEnumMap(original);
315     assertEquals(original, copy);
316   }
317 
318   public void testEnumMapWithInitialEmptyMap() {
319     Map<SomeEnum, Integer> original = Maps.newHashMap();
320     try {
321       Maps.newEnumMap(original);
322       fail("Empty map must result in an IllegalArgumentException");
323     } catch (IllegalArgumentException expected) {}
324   }
325 
326   public void testToStringImplWithNullKeys() throws Exception {
327     Map<String, String> hashmap = Maps.newHashMap();
328     hashmap.put("foo", "bar");
329     hashmap.put(null, "baz");
330 
331     assertEquals(hashmap.toString(), Maps.toStringImpl(hashmap));
332   }
333 
334   public void testToStringImplWithNullValues() throws Exception {
335     Map<String, String> hashmap = Maps.newHashMap();
336     hashmap.put("foo", "bar");
337     hashmap.put("baz", null);
338 
339     assertEquals(hashmap.toString(), Maps.toStringImpl(hashmap));
340   }
341 
342   @GwtIncompatible("NullPointerTester")
343   public void testNullPointerExceptions() {
344     new NullPointerTester().testAllPublicStaticMethods(Maps.class);
345   }
346 
347   private static final Map<Integer, Integer> EMPTY
348       = Collections.emptyMap();
349   private static final Map<Integer, Integer> SINGLETON
350       = Collections.singletonMap(1, 2);
351 
352   public void testMapDifferenceEmptyEmpty() {
353     MapDifference<Integer, Integer> diff = Maps.difference(EMPTY, EMPTY);
354     assertTrue(diff.areEqual());
355     assertEquals(EMPTY, diff.entriesOnlyOnLeft());
356     assertEquals(EMPTY, diff.entriesOnlyOnRight());
357     assertEquals(EMPTY, diff.entriesInCommon());
358     assertEquals(EMPTY, diff.entriesDiffering());
359     assertEquals("equal", diff.toString());
360   }
361 
362   public void testMapDifferenceEmptySingleton() {
363     MapDifference<Integer, Integer> diff = Maps.difference(EMPTY, SINGLETON);
364     assertFalse(diff.areEqual());
365     assertEquals(EMPTY, diff.entriesOnlyOnLeft());
366     assertEquals(SINGLETON, diff.entriesOnlyOnRight());
367     assertEquals(EMPTY, diff.entriesInCommon());
368     assertEquals(EMPTY, diff.entriesDiffering());
369     assertEquals("not equal: only on right={1=2}", diff.toString());
370   }
371 
372   public void testMapDifferenceSingletonEmpty() {
373     MapDifference<Integer, Integer> diff = Maps.difference(SINGLETON, EMPTY);
374     assertFalse(diff.areEqual());
375     assertEquals(SINGLETON, diff.entriesOnlyOnLeft());
376     assertEquals(EMPTY, diff.entriesOnlyOnRight());
377     assertEquals(EMPTY, diff.entriesInCommon());
378     assertEquals(EMPTY, diff.entriesDiffering());
379     assertEquals("not equal: only on left={1=2}", diff.toString());
380   }
381 
382   public void testMapDifferenceTypical() {
383     Map<Integer, String> left = ImmutableMap.of(
384         1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
385     Map<Integer, String> right = ImmutableMap.of(
386         1, "a", 3, "f", 5, "g", 6, "z");
387 
388     MapDifference<Integer, String> diff1 = Maps.difference(left, right);
389     assertFalse(diff1.areEqual());
390     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff1.entriesOnlyOnLeft());
391     assertEquals(ImmutableMap.of(6, "z"), diff1.entriesOnlyOnRight());
392     assertEquals(ImmutableMap.of(1, "a"), diff1.entriesInCommon());
393     assertEquals(ImmutableMap.of(3,
394         ValueDifferenceImpl.create("c", "f"), 5,
395         ValueDifferenceImpl.create("e", "g")),
396         diff1.entriesDiffering());
397     assertEquals("not equal: only on left={2=b, 4=d}: only on right={6=z}: "
398         + "value differences={3=(c, f), 5=(e, g)}", diff1.toString());
399 
400     MapDifference<Integer, String> diff2 = Maps.difference(right, left);
401     assertFalse(diff2.areEqual());
402     assertEquals(ImmutableMap.of(6, "z"), diff2.entriesOnlyOnLeft());
403     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff2.entriesOnlyOnRight());
404     assertEquals(ImmutableMap.of(1, "a"), diff2.entriesInCommon());
405     assertEquals(ImmutableMap.of(3,
406         ValueDifferenceImpl.create("f", "c"), 5,
407         ValueDifferenceImpl.create("g", "e")),
408         diff2.entriesDiffering());
409     assertEquals("not equal: only on left={6=z}: only on right={2=b, 4=d}: "
410         + "value differences={3=(f, c), 5=(g, e)}", diff2.toString());
411   }
412 
413   public void testMapDifferenceEquals() {
414     Map<Integer, String> left = ImmutableMap.of(
415         1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
416     Map<Integer, String> right = ImmutableMap.of(
417         1, "a", 3, "f", 5, "g", 6, "z");
418     Map<Integer, String> right2 = ImmutableMap.of(
419         1, "a", 3, "h", 5, "g", 6, "z");
420     MapDifference<Integer, String> original = Maps.difference(left, right);
421     MapDifference<Integer, String> same = Maps.difference(left, right);
422     MapDifference<Integer, String> reverse = Maps.difference(right, left);
423     MapDifference<Integer, String> diff2 = Maps.difference(left, right2);
424 
425     new EqualsTester()
426         .addEqualityGroup(original, same)
427         .addEqualityGroup(reverse)
428         .addEqualityGroup(diff2)
429         .testEquals();
430   }
431 
432   public void testMapDifferencePredicateTypical() {
433     Map<Integer, String> left = ImmutableMap.of(
434         1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
435     Map<Integer, String> right = ImmutableMap.of(
436         1, "A", 3, "F", 5, "G", 6, "Z");
437 
438     // TODO(kevinb): replace with Ascii.caseInsensitiveEquivalence() when it
439     // exists
440     Equivalence<String> caseInsensitiveEquivalence = Equivalence.equals().onResultOf(
441         new Function<String, String>() {
442           @Override public String apply(String input) {
443             return input.toLowerCase();
444           }
445         });
446 
447     MapDifference<Integer, String> diff1 = Maps.difference(left, right,
448         caseInsensitiveEquivalence);
449     assertFalse(diff1.areEqual());
450     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff1.entriesOnlyOnLeft());
451     assertEquals(ImmutableMap.of(6, "Z"), diff1.entriesOnlyOnRight());
452     assertEquals(ImmutableMap.of(1, "a"), diff1.entriesInCommon());
453     assertEquals(ImmutableMap.of(3,
454         ValueDifferenceImpl.create("c", "F"), 5,
455         ValueDifferenceImpl.create("e", "G")),
456         diff1.entriesDiffering());
457     assertEquals("not equal: only on left={2=b, 4=d}: only on right={6=Z}: "
458         + "value differences={3=(c, F), 5=(e, G)}", diff1.toString());
459 
460     MapDifference<Integer, String> diff2 = Maps.difference(right, left,
461         caseInsensitiveEquivalence);
462     assertFalse(diff2.areEqual());
463     assertEquals(ImmutableMap.of(6, "Z"), diff2.entriesOnlyOnLeft());
464     assertEquals(ImmutableMap.of(2, "b", 4, "d"), diff2.entriesOnlyOnRight());
465     assertEquals(ImmutableMap.of(1, "A"), diff2.entriesInCommon());
466     assertEquals(ImmutableMap.of(3,
467         ValueDifferenceImpl.create("F", "c"), 5,
468         ValueDifferenceImpl.create("G", "e")),
469         diff2.entriesDiffering());
470     assertEquals("not equal: only on left={6=Z}: only on right={2=b, 4=d}: "
471         + "value differences={3=(F, c), 5=(G, e)}", diff2.toString());
472   }
473 
474   private static final SortedMap<Integer, Integer> SORTED_EMPTY = Maps.newTreeMap();
475   private static final SortedMap<Integer, Integer> SORTED_SINGLETON =
476       ImmutableSortedMap.of(1, 2);
477 
478   public void testMapDifferenceOfSortedMapIsSorted() {
479     Map<Integer, Integer> map = SORTED_SINGLETON;
480     MapDifference<Integer, Integer> difference = Maps.difference(map, EMPTY);
481     assertTrue(difference instanceof SortedMapDifference);
482   }
483 
484   public void testSortedMapDifferenceEmptyEmpty() {
485     SortedMapDifference<Integer, Integer> diff =
486         Maps.difference(SORTED_EMPTY, SORTED_EMPTY);
487     assertTrue(diff.areEqual());
488     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnLeft());
489     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnRight());
490     assertEquals(SORTED_EMPTY, diff.entriesInCommon());
491     assertEquals(SORTED_EMPTY, diff.entriesDiffering());
492     assertEquals("equal", diff.toString());
493   }
494 
495   public void testSortedMapDifferenceEmptySingleton() {
496     SortedMapDifference<Integer, Integer> diff =
497         Maps.difference(SORTED_EMPTY, SORTED_SINGLETON);
498     assertFalse(diff.areEqual());
499     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnLeft());
500     assertEquals(SORTED_SINGLETON, diff.entriesOnlyOnRight());
501     assertEquals(SORTED_EMPTY, diff.entriesInCommon());
502     assertEquals(SORTED_EMPTY, diff.entriesDiffering());
503     assertEquals("not equal: only on right={1=2}", diff.toString());
504   }
505 
506   public void testSortedMapDifferenceSingletonEmpty() {
507     SortedMapDifference<Integer, Integer> diff =
508         Maps.difference(SORTED_SINGLETON, SORTED_EMPTY);
509     assertFalse(diff.areEqual());
510     assertEquals(SORTED_SINGLETON, diff.entriesOnlyOnLeft());
511     assertEquals(SORTED_EMPTY, diff.entriesOnlyOnRight());
512     assertEquals(SORTED_EMPTY, diff.entriesInCommon());
513     assertEquals(SORTED_EMPTY, diff.entriesDiffering());
514     assertEquals("not equal: only on left={1=2}", diff.toString());
515   }
516 
517   public void testSortedMapDifferenceTypical() {
518     SortedMap<Integer, String> left =
519         ImmutableSortedMap.<Integer, String>reverseOrder()
520         .put(1, "a").put(2, "b").put(3, "c").put(4, "d").put(5, "e")
521         .build();
522 
523     SortedMap<Integer, String> right =
524         ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z");
525 
526     SortedMapDifference<Integer, String> diff1 =
527         Maps.difference(left, right);
528     assertFalse(diff1.areEqual());
529     assertThat(diff1.entriesOnlyOnLeft().entrySet()).has().exactly(
530         Maps.immutableEntry(4, "d"), Maps.immutableEntry(2, "b")).inOrder();
531     assertThat(diff1.entriesOnlyOnRight().entrySet()).has().item(
532         Maps.immutableEntry(6, "z"));
533     assertThat(diff1.entriesInCommon().entrySet()).has().item(
534         Maps.immutableEntry(1, "a"));
535     assertThat(diff1.entriesDiffering().entrySet()).has().exactly(
536         Maps.immutableEntry(5, ValueDifferenceImpl.create("e", "g")),
537         Maps.immutableEntry(3, ValueDifferenceImpl.create("c", "f"))).inOrder();
538     assertEquals("not equal: only on left={4=d, 2=b}: only on right={6=z}: "
539         + "value differences={5=(e, g), 3=(c, f)}", diff1.toString());
540 
541     SortedMapDifference<Integer, String> diff2 =
542         Maps.difference(right, left);
543     assertFalse(diff2.areEqual());
544     assertThat(diff2.entriesOnlyOnLeft().entrySet()).has().item(
545         Maps.immutableEntry(6, "z"));
546     assertThat(diff2.entriesOnlyOnRight().entrySet()).has().exactly(
547         Maps.immutableEntry(2, "b"), Maps.immutableEntry(4, "d")).inOrder();
548     assertThat(diff1.entriesInCommon().entrySet()).has().item(
549         Maps.immutableEntry(1, "a"));
550     assertEquals(ImmutableMap.of(
551             3, ValueDifferenceImpl.create("f", "c"),
552             5, ValueDifferenceImpl.create("g", "e")),
553         diff2.entriesDiffering());
554     assertEquals("not equal: only on left={6=z}: only on right={2=b, 4=d}: "
555         + "value differences={3=(f, c), 5=(g, e)}", diff2.toString());
556   }
557 
558   public void testSortedMapDifferenceImmutable() {
559     SortedMap<Integer, String> left = Maps.newTreeMap(
560         ImmutableSortedMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e"));
561     SortedMap<Integer, String> right =
562         Maps.newTreeMap(ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z"));
563 
564     SortedMapDifference<Integer, String> diff1 =
565         Maps.difference(left, right);
566     left.put(6, "z");
567     assertFalse(diff1.areEqual());
568     assertThat(diff1.entriesOnlyOnLeft().entrySet()).has().exactly(
569         Maps.immutableEntry(2, "b"), Maps.immutableEntry(4, "d")).inOrder();
570     assertThat(diff1.entriesOnlyOnRight().entrySet()).has().item(
571         Maps.immutableEntry(6, "z"));
572     assertThat(diff1.entriesInCommon().entrySet()).has().item(
573         Maps.immutableEntry(1, "a"));
574     assertThat(diff1.entriesDiffering().entrySet()).has().exactly(
575         Maps.immutableEntry(3, ValueDifferenceImpl.create("c", "f")),
576         Maps.immutableEntry(5, ValueDifferenceImpl.create("e", "g"))).inOrder();
577     try {
578       diff1.entriesInCommon().put(7, "x");
579       fail();
580     } catch (UnsupportedOperationException expected) {
581     }
582     try {
583       diff1.entriesOnlyOnLeft().put(7, "x");
584       fail();
585     } catch (UnsupportedOperationException expected) {
586     }
587     try {
588       diff1.entriesOnlyOnRight().put(7, "x");
589       fail();
590     } catch (UnsupportedOperationException expected) {
591     }
592   }
593 
594   public void testSortedMapDifferenceEquals() {
595     SortedMap<Integer, String> left =
596         ImmutableSortedMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e");
597     SortedMap<Integer, String> right =
598         ImmutableSortedMap.of(1, "a", 3, "f", 5, "g", 6, "z");
599     SortedMap<Integer, String> right2 =
600         ImmutableSortedMap.of(1, "a", 3, "h", 5, "g", 6, "z");
601     SortedMapDifference<Integer, String> original =
602         Maps.difference(left, right);
603     SortedMapDifference<Integer, String> same =
604         Maps.difference(left, right);
605     SortedMapDifference<Integer, String> reverse =
606         Maps.difference(right, left);
607     SortedMapDifference<Integer, String> diff2 =
608         Maps.difference(left, right2);
609 
610     new EqualsTester()
611         .addEqualityGroup(original, same)
612         .addEqualityGroup(reverse)
613         .addEqualityGroup(diff2)
614         .testEquals();
615   }
616 
617   private static final Function<String, Integer> LENGTH_FUNCTION =
618       new Function<String, Integer>() {
619         @Override
620         public Integer apply(String input) {
621           return input.length();
622         }
623       };
624 
625   public void testAsMap() {
626     Set<String> strings = ImmutableSet.of("one", "two", "three");
627     Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
628     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
629     assertEquals(Integer.valueOf(5), map.get("three"));
630     assertNull(map.get("five"));
631     assertThat(map.entrySet()).has().exactly(
632         mapEntry("one", 3),
633         mapEntry("two", 3),
634         mapEntry("three", 5)).inOrder();
635   }
636 
637   public void testAsMapReadsThrough() {
638     Set<String> strings = Sets.newLinkedHashSet();
639     Collections.addAll(strings, "one", "two", "three");
640     Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
641     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
642     assertNull(map.get("four"));
643     strings.add("four");
644     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5, "four", 4), map);
645     assertEquals(Integer.valueOf(4), map.get("four"));
646   }
647 
648   public void testAsMapWritesThrough() {
649     Set<String> strings = Sets.newLinkedHashSet();
650     Collections.addAll(strings, "one", "two", "three");
651     Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
652     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
653     assertEquals(Integer.valueOf(3), map.remove("two"));
654     assertThat(strings).has().exactly("one", "three").inOrder();
655   }
656 
657   public void testAsMapEmpty() {
658     Set<String> strings = ImmutableSet.of();
659     Map<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
660     assertThat(map.entrySet()).isEmpty();
661     assertTrue(map.isEmpty());
662     assertNull(map.get("five"));
663   }
664 
665   private static class NonNavigableSortedSet
666       extends ForwardingSortedSet<String> {
667     private final SortedSet<String> delegate = Sets.newTreeSet();
668 
669     @Override
670     protected SortedSet<String> delegate() {
671       return delegate;
672     }
673   }
674 
675   public void testAsMapReturnsSortedMapForSortedSetInput() {
676     Set<String> set = new NonNavigableSortedSet();
677     assertTrue(Maps.asMap(set, Functions.identity()) instanceof SortedMap);
678   }
679 
680   public void testAsMapSorted() {
681     SortedSet<String> strings = new NonNavigableSortedSet();
682     Collections.addAll(strings, "one", "two", "three");
683     SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
684     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
685     assertEquals(Integer.valueOf(5), map.get("three"));
686     assertNull(map.get("five"));
687     assertThat(map.entrySet()).has().exactly(
688         mapEntry("one", 3),
689         mapEntry("three", 5),
690         mapEntry("two", 3)).inOrder();
691     assertThat(map.tailMap("onea").entrySet()).has().exactly(
692         mapEntry("three", 5),
693         mapEntry("two", 3)).inOrder();
694     assertThat(map.subMap("one", "two").entrySet()).has().exactly(
695         mapEntry("one", 3),
696         mapEntry("three", 5)).inOrder();
697   }
698 
699   public void testAsMapSortedReadsThrough() {
700     SortedSet<String> strings = new NonNavigableSortedSet();
701     Collections.addAll(strings, "one", "two", "three");
702     SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
703     assertNull(map.comparator());
704     assertEquals(ImmutableSortedMap.of("one", 3, "two", 3, "three", 5), map);
705     assertNull(map.get("four"));
706     strings.add("four");
707     assertEquals(
708         ImmutableSortedMap.of("one", 3, "two", 3, "three", 5, "four", 4),
709         map);
710     assertEquals(Integer.valueOf(4), map.get("four"));
711     SortedMap<String, Integer> headMap = map.headMap("two");
712     assertEquals(
713         ImmutableSortedMap.of("four", 4, "one", 3, "three", 5),
714         headMap);
715     strings.add("five");
716     strings.remove("one");
717     assertEquals(
718         ImmutableSortedMap.of("five", 4, "four", 4, "three", 5),
719         headMap);
720     assertThat(map.entrySet()).has().exactly(
721         mapEntry("five", 4),
722         mapEntry("four", 4),
723         mapEntry("three", 5),
724         mapEntry("two", 3)).inOrder();
725   }
726 
727   public void testAsMapSortedWritesThrough() {
728     SortedSet<String> strings = new NonNavigableSortedSet();
729     Collections.addAll(strings, "one", "two", "three");
730     SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
731     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
732     assertEquals(Integer.valueOf(3), map.remove("two"));
733     assertThat(strings).has().exactly("one", "three").inOrder();
734   }
735 
736   public void testAsMapSortedSubViewKeySetsDoNotSupportAdd() {
737     SortedMap<String, Integer> map = Maps.asMap(
738         new NonNavigableSortedSet(), LENGTH_FUNCTION);
739     try {
740       map.subMap("a", "z").keySet().add("a");
741       fail();
742     } catch (UnsupportedOperationException expected) {
743     }
744     try {
745       map.tailMap("a").keySet().add("a");
746       fail();
747     } catch (UnsupportedOperationException expected) {
748     }
749     try {
750       map.headMap("r").keySet().add("a");
751       fail();
752     } catch (UnsupportedOperationException expected) {
753     }
754     try {
755       map.headMap("r").tailMap("m").keySet().add("a");
756       fail();
757     } catch (UnsupportedOperationException expected) {
758     }
759   }
760 
761   public void testAsMapSortedEmpty() {
762     SortedSet<String> strings = new NonNavigableSortedSet();
763     SortedMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
764     assertThat(map.entrySet()).isEmpty();
765     assertTrue(map.isEmpty());
766     assertNull(map.get("five"));
767   }
768 
769   @GwtIncompatible("NavigableMap")
770   public void testAsMapReturnsNavigableMapForNavigableSetInput() {
771     Set<String> set = Sets.newTreeSet();
772     assertTrue(Maps.asMap(set, Functions.identity()) instanceof NavigableMap);
773   }
774 
775   @GwtIncompatible("NavigableMap")
776   public void testAsMapNavigable() {
777     NavigableSet<String> strings =
778         Sets.newTreeSet(asList("one", "two", "three"));
779     NavigableMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
780     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
781     assertEquals(Integer.valueOf(5), map.get("three"));
782     assertNull(map.get("five"));
783     assertThat(map.entrySet()).has().exactly(
784         mapEntry("one", 3),
785         mapEntry("three", 5),
786         mapEntry("two", 3)).inOrder();
787     assertThat(map.tailMap("onea").entrySet()).has().exactly(
788         mapEntry("three", 5),
789         mapEntry("two", 3)).inOrder();
790     assertThat(map.subMap("one", "two").entrySet()).has().exactly(
791         mapEntry("one", 3),
792         mapEntry("three", 5)).inOrder();
793 
794     assertEquals(ImmutableSortedMap.of("two", 3, "three", 5),
795         map.tailMap("three", true));
796     assertEquals(ImmutableSortedMap.of("one", 3, "three", 5),
797         map.headMap("two", false));
798     assertEquals(ImmutableSortedMap.of("three", 5),
799         map.subMap("one", false, "tr", true));
800 
801     assertEquals("three", map.higherKey("one"));
802     assertEquals("three", map.higherKey("r"));
803     assertEquals("three", map.ceilingKey("r"));
804     assertEquals("one", map.ceilingKey("one"));
805     assertEquals(mapEntry("three", 5), map.higherEntry("one"));
806     assertEquals(mapEntry("one", 3), map.ceilingEntry("one"));
807     assertEquals("one", map.lowerKey("three"));
808     assertEquals("one", map.lowerKey("r"));
809     assertEquals("one", map.floorKey("r"));
810     assertEquals("three", map.floorKey("three"));
811 
812     assertThat(map.descendingMap().entrySet()).has().exactly(
813         mapEntry("two", 3),
814         mapEntry("three", 5),
815         mapEntry("one", 3)).inOrder();
816     assertEquals(map.headMap("three", true),
817         map.descendingMap().tailMap("three", true));
818     assertThat(map.tailMap("three", false).entrySet()).has().item(
819         mapEntry("two", 3));
820     assertNull(map.tailMap("three", true).lowerEntry("three"));
821     assertThat(map.headMap("two", false).values()).has().exactly(3, 5).inOrder();
822     assertThat(map.headMap("two", false).descendingMap().values())
823         .has().exactly(5, 3).inOrder();
824     assertThat(map.descendingKeySet()).has().exactly(
825         "two", "three", "one").inOrder();
826 
827     assertEquals(mapEntry("one", 3), map.pollFirstEntry());
828     assertEquals(mapEntry("two", 3), map.pollLastEntry());
829     assertEquals(1, map.size());
830   }
831 
832   @GwtIncompatible("NavigableMap")
833   public void testAsMapNavigableReadsThrough() {
834     NavigableSet<String> strings = Sets.newTreeSet();
835     Collections.addAll(strings, "one", "two", "three");
836     NavigableMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
837     assertNull(map.comparator());
838     assertEquals(ImmutableSortedMap.of("one", 3, "two", 3, "three", 5), map);
839     assertNull(map.get("four"));
840     strings.add("four");
841     assertEquals(
842         ImmutableSortedMap.of("one", 3, "two", 3, "three", 5, "four", 4),
843         map);
844     assertEquals(Integer.valueOf(4), map.get("four"));
845     SortedMap<String, Integer> headMap = map.headMap("two");
846     assertEquals(
847         ImmutableSortedMap.of("four", 4, "one", 3, "three", 5),
848         headMap);
849     strings.add("five");
850     strings.remove("one");
851     assertEquals(
852         ImmutableSortedMap.of("five", 4, "four", 4, "three", 5),
853         headMap);
854     assertThat(map.entrySet()).has().exactly(
855         mapEntry("five", 4),
856         mapEntry("four", 4),
857         mapEntry("three", 5),
858         mapEntry("two", 3)).inOrder();
859 
860     NavigableMap<String, Integer> tailMap = map.tailMap("s", true);
861     NavigableMap<String, Integer> subMap = map.subMap("a", true, "t", false);
862 
863     strings.add("six");
864     strings.remove("two");
865     assertThat(tailMap.entrySet()).has().exactly(
866         mapEntry("six", 3),
867         mapEntry("three", 5)).inOrder();
868     assertThat(subMap.entrySet()).has().exactly(
869         mapEntry("five", 4),
870         mapEntry("four", 4),
871         mapEntry("six", 3)).inOrder();
872   }
873 
874   @GwtIncompatible("NavigableMap")
875   public void testAsMapNavigableWritesThrough() {
876     NavigableSet<String> strings = Sets.newTreeSet();
877     Collections.addAll(strings, "one", "two", "three");
878     NavigableMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
879     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
880     assertEquals(Integer.valueOf(3), map.remove("two"));
881     assertThat(strings).has().exactly("one", "three").inOrder();
882     assertEquals(mapEntry("three", 5),
883         map.subMap("one", false, "zzz", true).pollLastEntry());
884     assertThat(strings).has().item("one");
885   }
886 
887   @GwtIncompatible("NavigableMap")
888   public void testAsMapNavigableSubViewKeySetsDoNotSupportAdd() {
889     NavigableMap<String, Integer> map = Maps.asMap(
890         Sets.<String>newTreeSet(), LENGTH_FUNCTION);
891     try {
892       map.descendingKeySet().add("a");
893       fail();
894     } catch (UnsupportedOperationException expected) {
895     }
896     try {
897       map.subMap("a", true, "z", false).keySet().add("a");
898       fail();
899     } catch (UnsupportedOperationException expected) {
900     }
901     try {
902       map.tailMap("a", true).keySet().add("a");
903       fail();
904     } catch (UnsupportedOperationException expected) {
905     }
906     try {
907       map.headMap("r", true).keySet().add("a");
908       fail();
909     } catch (UnsupportedOperationException expected) {
910     }
911     try {
912       map.headMap("r", false).tailMap("m", true).keySet().add("a");
913       fail();
914     } catch (UnsupportedOperationException expected) {
915     }
916   }
917 
918   @GwtIncompatible("NavigableMap")
919   public void testAsMapNavigableEmpty() {
920     NavigableSet<String> strings = ImmutableSortedSet.of();
921     NavigableMap<String, Integer> map = Maps.asMap(strings, LENGTH_FUNCTION);
922     assertThat(map.entrySet()).isEmpty();
923     assertTrue(map.isEmpty());
924     assertNull(map.get("five"));
925   }
926 
927   public void testToMap() {
928     Iterable<String> strings = ImmutableList.of("one", "two", "three");
929     ImmutableMap<String, Integer> map = Maps.toMap(strings, LENGTH_FUNCTION);
930     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
931     assertThat(map.entrySet()).has().exactly(
932         mapEntry("one", 3),
933         mapEntry("two", 3),
934         mapEntry("three", 5)).inOrder();
935   }
936 
937   public void testToMapIterator() {
938     Iterator<String> strings = ImmutableList.of("one", "two", "three").iterator();
939     ImmutableMap<String, Integer> map = Maps.toMap(strings, LENGTH_FUNCTION);
940     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
941     assertThat(map.entrySet()).has().exactly(
942         mapEntry("one", 3),
943         mapEntry("two", 3),
944         mapEntry("three", 5)).inOrder();
945   }
946 
947   public void testToMapWithDuplicateKeys() {
948     Iterable<String> strings = ImmutableList.of("one", "two", "three", "two", "one");
949     ImmutableMap<String, Integer> map = Maps.toMap(strings, LENGTH_FUNCTION);
950     assertEquals(ImmutableMap.of("one", 3, "two", 3, "three", 5), map);
951     assertThat(map.entrySet()).has().exactly(
952         mapEntry("one", 3),
953         mapEntry("two", 3),
954         mapEntry("three", 5)).inOrder();
955   }
956 
957   public void testToMapWithNullKeys() {
958     Iterable<String> strings = Arrays.asList("one", null, "three");
959     try {
960       Maps.toMap(strings, Functions.constant("foo"));
961       fail();
962     } catch (NullPointerException expected) {
963     }
964   }
965 
966   public void testToMapWithNullValues() {
967     Iterable<String> strings = ImmutableList.of("one", "two", "three");
968     try {
969       Maps.toMap(strings, Functions.constant(null));
970       fail();
971     } catch (NullPointerException expected) {
972     }
973   }
974 
975   private static final BiMap<Integer, String> INT_TO_STRING_MAP =
976       new ImmutableBiMap.Builder<Integer, String>()
977           .put(1, "one")
978           .put(2, "two")
979           .put(3, "three")
980           .build();
981 
982   public void testUniqueIndexCollection() {
983     ImmutableMap<Integer, String> outputMap =
984         Maps.uniqueIndex(INT_TO_STRING_MAP.values(),
985             Functions.forMap(INT_TO_STRING_MAP.inverse()));
986     assertEquals(INT_TO_STRING_MAP, outputMap);
987   }
988 
989   public void testUniqueIndexIterable() {
990     ImmutableMap<Integer, String> outputMap =
991         Maps.uniqueIndex(new Iterable<String>() {
992           @Override
993           public Iterator<String> iterator() {
994             return INT_TO_STRING_MAP.values().iterator();
995           }
996         },
997         Functions.forMap(INT_TO_STRING_MAP.inverse()));
998     assertEquals(INT_TO_STRING_MAP, outputMap);
999   }
1000 
1001   public void testUniqueIndexIterator() {
1002     ImmutableMap<Integer, String> outputMap =
1003         Maps.uniqueIndex(INT_TO_STRING_MAP.values().iterator(),
1004             Functions.forMap(INT_TO_STRING_MAP.inverse()));
1005     assertEquals(INT_TO_STRING_MAP, outputMap);
1006   }
1007 
1008   /** Can't create the map if more than one value maps to the same key. */
1009   public void testUniqueIndexDuplicates() {
1010     try {
1011       Maps.uniqueIndex(ImmutableSet.of("one", "uno"), Functions.constant(1));
1012       fail();
1013     } catch (IllegalArgumentException expected) {
1014     }
1015   }
1016 
1017   /** Null values are not allowed. */
1018   public void testUniqueIndexNullValue() {
1019     List<String> listWithNull = Lists.newArrayList((String) null);
1020     try {
1021       Maps.uniqueIndex(listWithNull, Functions.constant(1));
1022       fail();
1023     } catch (NullPointerException expected) {
1024     }
1025   }
1026 
1027   /** Null keys aren't allowed either. */
1028   public void testUniqueIndexNullKey() {
1029     List<String> oneStringList = Lists.newArrayList("foo");
1030     try {
1031       Maps.uniqueIndex(oneStringList, Functions.constant(null));
1032       fail();
1033     } catch (NullPointerException expected) {
1034     }
1035   }
1036 
1037   @GwtIncompatible("Maps.fromProperties")
1038   @SuppressWarnings("deprecation") // StringBufferInputStream
1039   public void testFromProperties() throws IOException {
1040     Properties testProp = new Properties();
1041 
1042     Map<String, String> result = Maps.fromProperties(testProp);
1043     assertTrue(result.isEmpty());
1044     testProp.setProperty("first", "true");
1045 
1046     result = Maps.fromProperties(testProp);
1047     assertEquals("true", result.get("first"));
1048     assertEquals(1, result.size());
1049     testProp.setProperty("second", "null");
1050 
1051     result = Maps.fromProperties(testProp);
1052     assertEquals("true", result.get("first"));
1053     assertEquals("null", result.get("second"));
1054     assertEquals(2, result.size());
1055 
1056     // Now test values loaded from a stream.
1057     String props = "test\n second = 2\n Third item :   a short  phrase   ";
1058 
1059     testProp.load(new StringReader(props));
1060 
1061     result = Maps.fromProperties(testProp);
1062     assertEquals(4, result.size());
1063     assertEquals("true", result.get("first"));
1064     assertEquals("", result.get("test"));
1065     assertEquals("2", result.get("second"));
1066     assertEquals("item :   a short  phrase   ", result.get("Third"));
1067     assertFalse(result.containsKey("not here"));
1068 
1069     // Test loading system properties
1070     result = Maps.fromProperties(System.getProperties());
1071     assertTrue(result.containsKey("java.version"));
1072 
1073     // Test that defaults work, too.
1074     testProp = new Properties(System.getProperties());
1075     String override = "test\njava.version : hidden";
1076 
1077     testProp.load(new StringReader(override));
1078 
1079     result = Maps.fromProperties(testProp);
1080     assertTrue(result.size() > 2);
1081     assertEquals("", result.get("test"));
1082     assertEquals("hidden", result.get("java.version"));
1083     assertNotSame(System.getProperty("java.version"),
1084                   result.get("java.version"));
1085   }
1086 
1087   @GwtIncompatible("Maps.fromProperties")
1088   @SuppressWarnings("serial") // never serialized
1089   public void testFromPropertiesNullKey() {
1090     Properties properties = new Properties() {
1091       @Override public Enumeration<?> propertyNames() {
1092         return Iterators.asEnumeration(
1093             Arrays.asList(null, "first", "second").iterator());
1094       }
1095     };
1096     properties.setProperty("first", "true");
1097     properties.setProperty("second", "null");
1098 
1099     try {
1100       Maps.fromProperties(properties);
1101       fail();
1102     } catch (NullPointerException expected) {}
1103   }
1104 
1105   @GwtIncompatible("Maps.fromProperties")
1106   @SuppressWarnings("serial") // never serialized
1107   public void testFromPropertiesNonStringKeys() {
1108     Properties properties = new Properties() {
1109       @Override public Enumeration<?> propertyNames() {
1110         return Iterators.asEnumeration(
1111             Arrays.<Object>asList(Integer.valueOf(123), "first").iterator());
1112       }
1113     };
1114 
1115     try {
1116       Maps.fromProperties(properties);
1117       fail();
1118     } catch (ClassCastException expected) {}
1119   }
1120 
1121   public void testAsConverter_nominal() throws Exception {
1122     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
1123         "one", 1,
1124         "two", 2);
1125     Converter<String, Integer> converter = Maps.asConverter(biMap);
1126     for (Entry<String, Integer> entry : biMap.entrySet()) {
1127       assertSame(entry.getValue(), converter.convert(entry.getKey()));
1128     }
1129   }
1130 
1131   public void testAsConverter_inverse() throws Exception {
1132     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
1133         "one", 1,
1134         "two", 2);
1135     Converter<String, Integer> converter = Maps.asConverter(biMap);
1136     for (Entry<String, Integer> entry : biMap.entrySet()) {
1137       assertSame(entry.getKey(), converter.reverse().convert(entry.getValue()));
1138     }
1139   }
1140 
1141   public void testAsConverter_noMapping() throws Exception {
1142     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
1143         "one", 1,
1144         "two", 2);
1145     Converter<String, Integer> converter = Maps.asConverter(biMap);
1146     try {
1147       converter.convert("three");
1148       fail();
1149     } catch (IllegalArgumentException expected) {
1150     }
1151   }
1152 
1153   public void testAsConverter_nullConversions() throws Exception {
1154     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
1155         "one", 1,
1156         "two", 2);
1157     Converter<String, Integer> converter = Maps.asConverter(biMap);
1158     assertNull(converter.convert(null));
1159     assertNull(converter.reverse().convert(null));
1160   }
1161 
1162   public void testAsConverter_isAView() throws Exception {
1163     BiMap<String, Integer> biMap = HashBiMap.create();
1164     biMap.put("one", 1);
1165     biMap.put("two", 2);
1166     Converter<String, Integer> converter = Maps.asConverter(biMap);
1167 
1168     assertSame(1, converter.convert("one"));
1169     assertSame(2, converter.convert("two"));
1170     try {
1171       converter.convert("three");
1172       fail();
1173     } catch (IllegalArgumentException expected) {
1174     }
1175 
1176     biMap.put("three", 3);
1177 
1178     assertSame(1, converter.convert("one"));
1179     assertSame(2, converter.convert("two"));
1180     assertSame(3, converter.convert("three"));
1181   }
1182 
1183   public void testAsConverter_withNullMapping() throws Exception {
1184     BiMap<String, Integer> biMap = HashBiMap.create();
1185     biMap.put("one", 1);
1186     biMap.put("two", 2);
1187     biMap.put("three", null);
1188     try {
1189       Maps.asConverter(biMap).convert("three");
1190       fail();
1191     } catch (IllegalArgumentException expected) {
1192     }
1193   }
1194 
1195   public void testAsConverter_toString() {
1196     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
1197         "one", 1,
1198         "two", 2);
1199     Converter<String, Integer> converter = Maps.asConverter(biMap);
1200     assertEquals("Maps.asConverter({one=1, two=2})", converter.toString());
1201   }
1202 
1203   public void testAsConverter_serialization() {
1204     ImmutableBiMap<String, Integer> biMap = ImmutableBiMap.of(
1205         "one", 1,
1206         "two", 2);
1207     Converter<String, Integer> converter = Maps.asConverter(biMap);
1208     SerializableTester.reserializeAndAssert(converter);
1209   }
1210 
1211   public void testUnmodifiableBiMap() {
1212     BiMap<Integer, String> mod = HashBiMap.create();
1213     mod.put(1, "one");
1214     mod.put(2, "two");
1215     mod.put(3, "three");
1216 
1217     BiMap<Number, String> unmod = Maps.<Number, String>unmodifiableBiMap(mod);
1218 
1219     /* No aliasing on inverse operations. */
1220     assertSame(unmod.inverse(), unmod.inverse());
1221     assertSame(unmod, unmod.inverse().inverse());
1222 
1223     /* Unmodifiable is a view. */
1224     mod.put(4, "four");
1225     assertEquals(true, unmod.get(4).equals("four"));
1226     assertEquals(true, unmod.inverse().get("four").equals(4));
1227 
1228     /* UnsupportedOperationException on direct modifications. */
1229     try {
1230       unmod.put(4, "four");
1231       fail("UnsupportedOperationException expected");
1232     } catch (UnsupportedOperationException expected) {}
1233     try {
1234       unmod.forcePut(4, "four");
1235       fail("UnsupportedOperationException expected");
1236     } catch (UnsupportedOperationException expected) {}
1237     try {
1238       unmod.putAll(Collections.singletonMap(4, "four"));
1239       fail("UnsupportedOperationException expected");
1240     } catch (UnsupportedOperationException expected) {}
1241 
1242     /* UnsupportedOperationException on indirect modifications. */
1243     BiMap<String, Number> inverse = unmod.inverse();
1244     try {
1245       inverse.put("four", 4);
1246       fail("UnsupportedOperationException expected");
1247     } catch (UnsupportedOperationException expected) {}
1248     try {
1249       inverse.forcePut("four", 4);
1250       fail("UnsupportedOperationException expected");
1251     } catch (UnsupportedOperationException expected) {}
1252     try {
1253       inverse.putAll(Collections.singletonMap("four", 4));
1254       fail("UnsupportedOperationException expected");
1255     } catch (UnsupportedOperationException expected) {}
1256     Set<String> values = unmod.values();
1257     try {
1258       values.remove("four");
1259       fail("UnsupportedOperationException expected");
1260     } catch (UnsupportedOperationException expected) {}
1261     Set<Map.Entry<Number, String>> entries = unmod.entrySet();
1262     Map.Entry<Number, String> entry = entries.iterator().next();
1263     try {
1264       entry.setValue("four");
1265       fail("UnsupportedOperationException expected");
1266     } catch (UnsupportedOperationException expected) {}
1267     @SuppressWarnings("unchecked")
1268     Map.Entry<Integer, String> entry2
1269         = (Map.Entry<Integer, String>) entries.toArray()[0];
1270     try {
1271       entry2.setValue("four");
1272       fail("UnsupportedOperationException expected");
1273     } catch (UnsupportedOperationException expected) {}
1274   }
1275 
1276   public void testImmutableEntry() {
1277     Map.Entry<String, Integer> e = Maps.immutableEntry("foo", 1);
1278     assertEquals("foo", e.getKey());
1279     assertEquals(1, (int) e.getValue());
1280     try {
1281       e.setValue(2);
1282       fail("UnsupportedOperationException expected");
1283     } catch (UnsupportedOperationException expected) {}
1284     assertEquals("foo=1", e.toString());
1285     assertEquals(101575, e.hashCode());
1286   }
1287 
1288   public void testImmutableEntryNull() {
1289     Map.Entry<String, Integer> e
1290         = Maps.immutableEntry((String) null, (Integer) null);
1291     assertNull(e.getKey());
1292     assertNull(e.getValue());
1293     try {
1294       e.setValue(null);
1295       fail("UnsupportedOperationException expected");
1296     } catch (UnsupportedOperationException expected) {}
1297     assertEquals("null=null", e.toString());
1298     assertEquals(0, e.hashCode());
1299   }
1300 
1301   /** See {@link SynchronizedBiMapTest} for more tests. */
1302   public void testSynchronizedBiMap() {
1303     BiMap<String, Integer> bimap = HashBiMap.create();
1304     bimap.put("one", 1);
1305     BiMap<String, Integer> sync = Maps.synchronizedBiMap(bimap);
1306     bimap.put("two", 2);
1307     sync.put("three", 3);
1308     assertEquals(ImmutableSet.of(1, 2, 3), bimap.inverse().keySet());
1309     assertEquals(ImmutableSet.of(1, 2, 3), sync.inverse().keySet());
1310   }
1311 
1312   private static final Predicate<String> NOT_LENGTH_3
1313       = new Predicate<String>() {
1314         @Override
1315         public boolean apply(String input) {
1316           return input == null || input.length() != 3;
1317         }
1318       };
1319 
1320   private static final Predicate<Integer> EVEN
1321       = new Predicate<Integer>() {
1322         @Override
1323         public boolean apply(Integer input) {
1324           return input == null || input % 2 == 0;
1325         }
1326       };
1327 
1328   private static final Predicate<Entry<String, Integer>> CORRECT_LENGTH
1329       = new Predicate<Entry<String, Integer>>() {
1330         @Override
1331         public boolean apply(Entry<String, Integer> input) {
1332           return input.getKey().length() == input.getValue();
1333         }
1334       };
1335 
1336   private static final Function<Integer, Double> SQRT_FUNCTION = new Function<Integer, Double>() {
1337       @Override
1338       public Double apply(Integer in) {
1339         return Math.sqrt(in);
1340       }
1341     };
1342 
1343   public static class FilteredMapTest extends TestCase {
1344     Map<String, Integer> createUnfiltered() {
1345       return Maps.newHashMap();
1346     }
1347 
1348     public void testFilteredKeysIllegalPut() {
1349       Map<String, Integer> unfiltered = createUnfiltered();
1350       Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
1351       filtered.put("a", 1);
1352       filtered.put("b", 2);
1353       assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
1354 
1355       try {
1356         filtered.put("yyy", 3);
1357         fail();
1358       } catch (IllegalArgumentException expected) {}
1359     }
1360 
1361     public void testFilteredKeysIllegalPutAll() {
1362       Map<String, Integer> unfiltered = createUnfiltered();
1363       Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
1364       filtered.put("a", 1);
1365       filtered.put("b", 2);
1366       assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
1367 
1368       try {
1369         filtered.putAll(ImmutableMap.of("c", 3, "zzz", 4, "b", 5));
1370         fail();
1371       } catch (IllegalArgumentException expected) {}
1372 
1373       assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered);
1374     }
1375 
1376     public void testFilteredKeysFilteredReflectsBackingChanges() {
1377       Map<String, Integer> unfiltered = createUnfiltered();
1378       Map<String, Integer> filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3);
1379       unfiltered.put("two", 2);
1380       unfiltered.put("three", 3);
1381       unfiltered.put("four", 4);
1382       assertEquals(ImmutableMap.of("two", 2, "three", 3, "four", 4), unfiltered);
1383       assertEquals(ImmutableMap.of("three", 3, "four", 4), filtered);
1384 
1385       unfiltered.remove("three");
1386       assertEquals(ImmutableMap.of("two", 2, "four", 4), unfiltered);
1387       assertEquals(ImmutableMap.of("four", 4), filtered);
1388 
1389       unfiltered.clear();
1390       assertEquals(ImmutableMap.of(), unfiltered);
1391       assertEquals(ImmutableMap.of(), filtered);
1392     }
1393 
1394     public void testFilteredValuesIllegalPut() {
1395       Map<String, Integer> unfiltered = createUnfiltered();
1396       Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1397       filtered.put("a", 2);
1398       unfiltered.put("b", 4);
1399       unfiltered.put("c", 5);
1400       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1401 
1402       try {
1403         filtered.put("yyy", 3);
1404         fail();
1405       } catch (IllegalArgumentException expected) {}
1406       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1407     }
1408 
1409     public void testFilteredValuesIllegalPutAll() {
1410       Map<String, Integer> unfiltered = createUnfiltered();
1411       Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1412       filtered.put("a", 2);
1413       unfiltered.put("b", 4);
1414       unfiltered.put("c", 5);
1415       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1416 
1417       try {
1418         filtered.putAll(ImmutableMap.of("c", 4, "zzz", 5, "b", 6));
1419         fail();
1420       } catch (IllegalArgumentException expected) {}
1421       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1422     }
1423 
1424     public void testFilteredValuesIllegalSetValue() {
1425       Map<String, Integer> unfiltered = createUnfiltered();
1426       Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1427       filtered.put("a", 2);
1428       filtered.put("b", 4);
1429       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1430 
1431       Entry<String, Integer> entry = filtered.entrySet().iterator().next();
1432       try {
1433         entry.setValue(5);
1434         fail();
1435       } catch (IllegalArgumentException expected) {}
1436 
1437       assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered);
1438     }
1439 
1440     public void testFilteredValuesClear() {
1441       Map<String, Integer> unfiltered = createUnfiltered();
1442       unfiltered.put("one", 1);
1443       unfiltered.put("two", 2);
1444       unfiltered.put("three", 3);
1445       unfiltered.put("four", 4);
1446       Map<String, Integer> filtered = Maps.filterValues(unfiltered, EVEN);
1447       assertEquals(ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4),
1448           unfiltered);
1449       assertEquals(ImmutableMap.of("two", 2, "four", 4), filtered);
1450 
1451       filtered.clear();
1452       assertEquals(ImmutableMap.of("one", 1, "three", 3), unfiltered);
1453       assertTrue(filtered.isEmpty());
1454     }
1455 
1456     public void testFilteredEntriesIllegalPut() {
1457       Map<String, Integer> unfiltered = createUnfiltered();
1458       unfiltered.put("cat", 3);
1459       unfiltered.put("dog", 2);
1460       unfiltered.put("horse", 5);
1461       Map<String, Integer> filtered
1462           = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1463       assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered);
1464 
1465       filtered.put("chicken", 7);
1466       assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1467 
1468       try {
1469         filtered.put("cow", 7);
1470         fail();
1471       } catch (IllegalArgumentException expected) {}
1472       assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1473     }
1474 
1475     public void testFilteredEntriesIllegalPutAll() {
1476       Map<String, Integer> unfiltered = createUnfiltered();
1477       unfiltered.put("cat", 3);
1478       unfiltered.put("dog", 2);
1479       unfiltered.put("horse", 5);
1480       Map<String, Integer> filtered
1481           = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1482       assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered);
1483 
1484       filtered.put("chicken", 7);
1485       assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1486 
1487       try {
1488         filtered.putAll(ImmutableMap.of("sheep", 5, "cow", 7));
1489         fail();
1490       } catch (IllegalArgumentException expected) {}
1491       assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered);
1492     }
1493 
1494     public void testFilteredEntriesObjectPredicate() {
1495       Map<String, Integer> unfiltered = createUnfiltered();
1496       unfiltered.put("cat", 3);
1497       unfiltered.put("dog", 2);
1498       unfiltered.put("horse", 5);
1499       Predicate<Object> predicate = Predicates.alwaysFalse();
1500       Map<String, Integer> filtered
1501           = Maps.filterEntries(unfiltered, predicate);
1502       assertTrue(filtered.isEmpty());
1503     }
1504 
1505     public void testFilteredEntriesWildCardEntryPredicate() {
1506       Map<String, Integer> unfiltered = createUnfiltered();
1507       unfiltered.put("cat", 3);
1508       unfiltered.put("dog", 2);
1509       unfiltered.put("horse", 5);
1510       Predicate<Entry<?, ?>> predicate = new Predicate<Entry<?, ?>>() {
1511         @Override
1512         public boolean apply(Entry<?, ?> input) {
1513           return "cat".equals(input.getKey())
1514               || Integer.valueOf(2) == input.getValue();
1515         }
1516       };
1517       Map<String, Integer> filtered
1518           = Maps.filterEntries(unfiltered, predicate);
1519       assertEquals(ImmutableMap.of("cat", 3, "dog", 2), filtered);
1520     }
1521   }
1522 
1523   public static class FilteredSortedMapTest extends FilteredMapTest {
1524     @Override
1525     SortedMap<String, Integer> createUnfiltered() {
1526       return Maps.newTreeMap();
1527     }
1528 
1529     public void testFilterKeysIdentifiesSortedMap() {
1530       SortedMap<String, Integer> map = createUnfiltered();
1531       assertTrue(Maps.filterKeys((Map<String, Integer>) map, NOT_LENGTH_3)
1532           instanceof SortedMap);
1533     }
1534 
1535     public void testFilterValuesIdentifiesSortedMap() {
1536       SortedMap<String, Integer> map = createUnfiltered();
1537       assertTrue(Maps.filterValues((Map<String, Integer>) map, EVEN)
1538           instanceof SortedMap);
1539     }
1540 
1541     public void testFilterEntriesIdentifiesSortedMap() {
1542       SortedMap<String, Integer> map = createUnfiltered();
1543       assertTrue(Maps.filterEntries((Map<String, Integer>) map, CORRECT_LENGTH)
1544           instanceof SortedMap);
1545     }
1546 
1547     public void testFirstAndLastKeyFilteredMap() {
1548       SortedMap<String, Integer> unfiltered = createUnfiltered();
1549       unfiltered.put("apple", 2);
1550       unfiltered.put("banana", 6);
1551       unfiltered.put("cat", 3);
1552       unfiltered.put("dog", 5);
1553 
1554       SortedMap<String, Integer> filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1555       assertEquals("banana", filtered.firstKey());
1556       assertEquals("cat", filtered.lastKey());
1557     }
1558 
1559     public void testHeadSubTailMap_FilteredMap() {
1560       SortedMap<String, Integer> unfiltered = createUnfiltered();
1561       unfiltered.put("apple", 2);
1562       unfiltered.put("banana", 6);
1563       unfiltered.put("cat", 4);
1564       unfiltered.put("dog", 3);
1565       SortedMap<String, Integer> filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH);
1566 
1567       assertEquals(ImmutableMap.of("banana", 6), filtered.headMap("dog"));
1568       assertEquals(ImmutableMap.of(), filtered.headMap("banana"));
1569       assertEquals(ImmutableMap.of("banana", 6, "dog", 3), filtered.headMap("emu"));
1570 
1571       assertEquals(ImmutableMap.of("banana", 6), filtered.subMap("banana", "dog"));
1572       assertEquals(ImmutableMap.of("dog", 3), filtered.subMap("cat", "emu"));
1573 
1574       assertEquals(ImmutableMap.of("dog", 3), filtered.tailMap("cat"));
1575       assertEquals(ImmutableMap.of("banana", 6, "dog", 3), filtered.tailMap("banana"));
1576     }
1577   }
1578 
1579   public static class FilteredBiMapTest extends FilteredMapTest {
1580     @Override
1581     BiMap<String, Integer> createUnfiltered() {
1582       return HashBiMap.create();
1583     }
1584 
1585     public void testFilterKeysIdentifiesBiMap() {
1586       BiMap<String, Integer> map = createUnfiltered();
1587       assertTrue(Maps.filterKeys((Map<String, Integer>) map, NOT_LENGTH_3)
1588           instanceof BiMap);
1589     }
1590 
1591     public void testFilterValuesIdentifiesBiMap() {
1592       BiMap<String, Integer> map = createUnfiltered();
1593       assertTrue(Maps.filterValues((Map<String, Integer>) map, EVEN)
1594           instanceof BiMap);
1595     }
1596 
1597     public void testFilterEntriesIdentifiesBiMap() {
1598       BiMap<String, Integer> map = createUnfiltered();
1599       assertTrue(Maps.filterEntries((Map<String, Integer>) map, CORRECT_LENGTH)
1600           instanceof BiMap);
1601     }
1602   }
1603 
1604   public void testTransformValues() {
1605     Map<String, Integer> map = ImmutableMap.of("a", 4, "b", 9);
1606     Map<String, Double> transformed = transformValues(map, SQRT_FUNCTION);
1607 
1608     assertEquals(ImmutableMap.of("a", 2.0, "b", 3.0), transformed);
1609   }
1610 
1611   public void testTransformValuesSecretlySorted() {
1612     Map<String, Integer> map =
1613         sortedNotNavigable(ImmutableSortedMap.of("a", 4, "b", 9));
1614     Map<String, Double> transformed = transformValues(map, SQRT_FUNCTION);
1615 
1616     assertEquals(ImmutableMap.of("a", 2.0, "b", 3.0), transformed);
1617     assertTrue(transformed instanceof SortedMap);
1618   }
1619 
1620   @GwtIncompatible("NavigableMap")
1621   public void testTransformValuesSecretlyNavigable() {
1622     Map<String, Integer> map = ImmutableSortedMap.of("a", 4, "b", 9);
1623     Map<String, Double> transformed;
1624 
1625     transformed = transformValues(map, SQRT_FUNCTION);
1626     assertEquals(ImmutableMap.of("a", 2.0, "b", 3.0), transformed);
1627     assertTrue(transformed instanceof NavigableMap);
1628 
1629     transformed =
1630         transformValues((SortedMap<String, Integer>) map, SQRT_FUNCTION);
1631     assertEquals(ImmutableMap.of("a", 2.0, "b", 3.0), transformed);
1632     assertTrue(transformed instanceof NavigableMap);
1633   }
1634 
1635   public void testTransformEntries() {
1636     Map<String, String> map = ImmutableMap.of("a", "4", "b", "9");
1637     EntryTransformer<String, String, String> concat =
1638         new EntryTransformer<String, String, String>() {
1639           @Override
1640           public String transformEntry(String key, String value) {
1641             return key + value;
1642           }
1643         };
1644     Map<String, String> transformed = transformEntries(map, concat);
1645 
1646     assertEquals(ImmutableMap.of("a", "a4", "b", "b9"), transformed);
1647   }
1648 
1649   public void testTransformEntriesSecretlySorted() {
1650     Map<String, String> map = ImmutableSortedMap.of("a", "4", "b", "9");
1651     EntryTransformer<String, String, String> concat =
1652         new EntryTransformer<String, String, String>() {
1653           @Override
1654           public String transformEntry(String key, String value) {
1655             return key + value;
1656           }
1657         };
1658     Map<String, String> transformed = transformEntries(map, concat);
1659 
1660     assertEquals(ImmutableMap.of("a", "a4", "b", "b9"), transformed);
1661     assertTrue(transformed instanceof SortedMap);
1662   }
1663 
1664   @GwtIncompatible("NavigableMap")
1665   public void testTransformEntriesSecretlyNavigable() {
1666     Map<String, String> map = ImmutableSortedMap.of("a", "4", "b", "9");
1667     EntryTransformer<String, String, String> concat =
1668         new EntryTransformer<String, String, String>() {
1669           @Override
1670           public String transformEntry(String key, String value) {
1671             return key + value;
1672           }
1673         };
1674     Map<String, String> transformed;
1675 
1676     transformed = transformEntries(map, concat);
1677     assertEquals(ImmutableMap.of("a", "a4", "b", "b9"), transformed);
1678     assertTrue(transformed instanceof NavigableMap);
1679 
1680     transformed = transformEntries((SortedMap<String, String>) map, concat);
1681     assertEquals(ImmutableMap.of("a", "a4", "b", "b9"), transformed);
1682     assertTrue(transformed instanceof NavigableMap);
1683   }
1684 
1685   @SuppressWarnings("unused")
1686   public void testTransformEntriesGenerics() {
1687     Map<Object, Object> map1 = ImmutableMap.<Object, Object>of(1, 2);
1688     Map<Object, Number> map2 = ImmutableMap.<Object, Number>of(1, 2);
1689     Map<Object, Integer> map3 = ImmutableMap.<Object, Integer>of(1, 2);
1690     Map<Number, Object> map4 = ImmutableMap.<Number, Object>of(1, 2);
1691     Map<Number, Number> map5 = ImmutableMap.<Number, Number>of(1, 2);
1692     Map<Number, Integer> map6 = ImmutableMap.<Number, Integer>of(1, 2);
1693     Map<Integer, Object> map7 = ImmutableMap.<Integer, Object>of(1, 2);
1694     Map<Integer, Number> map8 = ImmutableMap.<Integer, Number>of(1, 2);
1695     Map<Integer, Integer> map9 = ImmutableMap.<Integer, Integer>of(1, 2);
1696     Map<? extends Number, ? extends Number> map0 = ImmutableMap.of(1, 2);
1697 
1698     EntryTransformer<Number, Number, Double> transformer =
1699         new EntryTransformer<Number, Number, Double>() {
1700           @Override
1701           public Double transformEntry(Number key, Number value) {
1702             return key.doubleValue() + value.doubleValue();
1703           }
1704         };
1705 
1706     Map<Object, Double> objectKeyed;
1707     Map<Number, Double> numberKeyed;
1708     Map<Integer, Double> integerKeyed;
1709 
1710     numberKeyed = transformEntries(map5, transformer);
1711     numberKeyed = transformEntries(map6, transformer);
1712     integerKeyed = transformEntries(map8, transformer);
1713     integerKeyed = transformEntries(map9, transformer);
1714 
1715     Map<? extends Number, Double> wildcarded = transformEntries(map0, transformer);
1716 
1717     // Can't loosen the key type:
1718     // objectKeyed = transformEntries(map5, transformer);
1719     // objectKeyed = transformEntries(map6, transformer);
1720     // objectKeyed = transformEntries(map8, transformer);
1721     // objectKeyed = transformEntries(map9, transformer);
1722     // numberKeyed = transformEntries(map8, transformer);
1723     // numberKeyed = transformEntries(map9, transformer);
1724 
1725     // Can't loosen the value type:
1726     // Map<Number, Number> looseValued1 = transformEntries(map5, transformer);
1727     // Map<Number, Number> looseValued2 = transformEntries(map6, transformer);
1728     // Map<Integer, Number> looseValued3 = transformEntries(map8, transformer);
1729     // Map<Integer, Number> looseValued4 = transformEntries(map9, transformer);
1730 
1731     // Can't call with too loose a key:
1732     // transformEntries(map1, transformer);
1733     // transformEntries(map2, transformer);
1734     // transformEntries(map3, transformer);
1735 
1736     // Can't call with too loose a value:
1737     // transformEntries(map1, transformer);
1738     // transformEntries(map4, transformer);
1739     // transformEntries(map7, transformer);
1740   }
1741 
1742   public void testTransformEntriesExample() {
1743     Map<String, Boolean> options =
1744         ImmutableMap.of("verbose", true, "sort", false);
1745     EntryTransformer<String, Boolean, String> flagPrefixer =
1746         new EntryTransformer<String, Boolean, String>() {
1747           @Override
1748           public String transformEntry(String key, Boolean value) {
1749             return value ? key : "no" + key;
1750           }
1751         };
1752     Map<String, String> transformed = transformEntries(options, flagPrefixer);
1753     assertEquals("{verbose=verbose, sort=nosort}", transformed.toString());
1754   }
1755 
1756   // Logically this would accept a NavigableMap, but that won't work under GWT.
1757   private static <K, V> SortedMap<K, V> sortedNotNavigable(
1758       final SortedMap<K, V> map) {
1759     return new ForwardingSortedMap<K, V>() {
1760       @Override protected SortedMap<K, V> delegate() {
1761         return map;
1762       }
1763     };
1764   }
1765 
1766   public void testSortedMapTransformValues() {
1767     SortedMap<String, Integer> map =
1768         sortedNotNavigable(ImmutableSortedMap.of("a", 4, "b", 9));
1769     SortedMap<String, Double> transformed =
1770         transformValues(map, SQRT_FUNCTION);
1771 
1772     /*
1773      * We'd like to sanity check that we didn't get a NavigableMap out, but we
1774      * can't easily do so while maintaining GWT compatibility.
1775      */
1776     assertEquals(ImmutableSortedMap.of("a", 2.0, "b", 3.0), transformed);
1777   }
1778 
1779   @GwtIncompatible("NavigableMap")
1780   public void testNavigableMapTransformValues() {
1781     NavigableMap<String, Integer> map = ImmutableSortedMap.of("a", 4, "b", 9);
1782     NavigableMap<String, Double> transformed =
1783         transformValues(map, SQRT_FUNCTION);
1784 
1785     assertEquals(ImmutableSortedMap.of("a", 2.0, "b", 3.0), transformed);
1786   }
1787 
1788   public void testSortedMapTransformEntries() {
1789     SortedMap<String, String> map =
1790         sortedNotNavigable(ImmutableSortedMap.of("a", "4", "b", "9"));
1791     EntryTransformer<String, String, String> concat =
1792         new EntryTransformer<String, String, String>() {
1793           @Override
1794           public String transformEntry(String key, String value) {
1795             return key + value;
1796           }
1797         };
1798     SortedMap<String, String> transformed = transformEntries(map, concat);
1799 
1800     /*
1801      * We'd like to sanity check that we didn't get a NavigableMap out, but we
1802      * can't easily do so while maintaining GWT compatibility.
1803      */
1804     assertEquals(ImmutableSortedMap.of("a", "a4", "b", "b9"), transformed);
1805   }
1806 
1807   @GwtIncompatible("NavigableMap")
1808   public void testNavigableMapTransformEntries() {
1809     NavigableMap<String, String> map =
1810         ImmutableSortedMap.of("a", "4", "b", "9");
1811     EntryTransformer<String, String, String> concat =
1812         new EntryTransformer<String, String, String>() {
1813           @Override
1814           public String transformEntry(String key, String value) {
1815             return key + value;
1816           }
1817         };
1818     NavigableMap<String, String> transformed = transformEntries(map, concat);
1819 
1820     assertEquals(ImmutableSortedMap.of("a", "a4", "b", "b9"), transformed);
1821   }
1822 
1823   @GwtIncompatible("NavigableMap")
1824   public void testUnmodifiableNavigableMap() {
1825     TreeMap<Integer, String> mod = Maps.newTreeMap();
1826     mod.put(1, "one");
1827     mod.put(2, "two");
1828     mod.put(3, "three");
1829 
1830     NavigableMap<Integer, String> unmod = unmodifiableNavigableMap(mod);
1831 
1832     /* unmod is a view. */
1833     mod.put(4, "four");
1834     assertEquals("four", unmod.get(4));
1835     assertEquals("four", unmod.descendingMap().get(4));
1836 
1837     ensureNotDirectlyModifiable(unmod);
1838     ensureNotDirectlyModifiable(unmod.descendingMap());
1839     ensureNotDirectlyModifiable(unmod.headMap(2, true));
1840     ensureNotDirectlyModifiable(unmod.subMap(1, true, 3, true));
1841     ensureNotDirectlyModifiable(unmod.tailMap(2, true));
1842 
1843     Collection<String> values = unmod.values();
1844     try {
1845       values.add("4");
1846       fail("UnsupportedOperationException expected");
1847     } catch (UnsupportedOperationException expected) {
1848     }
1849     try {
1850       values.remove("four");
1851       fail("UnsupportedOperationException expected");
1852     } catch (UnsupportedOperationException expected) {
1853     }
1854     try {
1855       values.removeAll(Collections.singleton("four"));
1856       fail("UnsupportedOperationException expected");
1857     } catch (UnsupportedOperationException expected) {
1858     }
1859     try {
1860       values.retainAll(Collections.singleton("four"));
1861       fail("UnsupportedOperationException expected");
1862     } catch (UnsupportedOperationException expected) {
1863     }
1864     try {
1865       Iterator<String> iterator = values.iterator();
1866       iterator.next();
1867       iterator.remove();
1868       fail("UnsupportedOperationException expected");
1869     } catch (UnsupportedOperationException expected) {
1870     }
1871 
1872     Set<Map.Entry<Integer, String>> entries = unmod.entrySet();
1873     try {
1874       Iterator<Map.Entry<Integer, String>> iterator = entries.iterator();
1875       iterator.next();
1876       iterator.remove();
1877       fail("UnsupportedOperationException expected");
1878     } catch (UnsupportedOperationException expected) {
1879     }
1880     Map.Entry<Integer, String> entry = entries.iterator().next();
1881     try {
1882       entry.setValue("four");
1883       fail("UnsupportedOperationException expected");
1884     } catch (UnsupportedOperationException expected) {
1885     }
1886     entry = unmod.lowerEntry(1);
1887     assertNull(entry);
1888     entry = unmod.floorEntry(2);
1889     try {
1890       entry.setValue("four");
1891       fail("UnsupportedOperationException expected");
1892     } catch (UnsupportedOperationException expected) {
1893     }
1894     entry = unmod.ceilingEntry(2);
1895     try {
1896       entry.setValue("four");
1897       fail("UnsupportedOperationException expected");
1898     } catch (UnsupportedOperationException expected) {
1899     }
1900     entry = unmod.lowerEntry(2);
1901     try {
1902       entry.setValue("four");
1903       fail("UnsupportedOperationException expected");
1904     } catch (UnsupportedOperationException expected) {
1905     }
1906     entry = unmod.higherEntry(2);
1907     try {
1908       entry.setValue("four");
1909       fail("UnsupportedOperationException expected");
1910     } catch (UnsupportedOperationException expected) {
1911     }
1912     entry = unmod.firstEntry();
1913     try {
1914       entry.setValue("four");
1915       fail("UnsupportedOperationException expected");
1916     } catch (UnsupportedOperationException expected) {
1917     }
1918     entry = unmod.lastEntry();
1919     try {
1920       entry.setValue("four");
1921       fail("UnsupportedOperationException expected");
1922     } catch (UnsupportedOperationException expected) {
1923     }
1924         @SuppressWarnings("unchecked")
1925     Map.Entry<Integer, String> entry2 =
1926         (Map.Entry<Integer, String>) entries.toArray()[0];
1927     try {
1928       entry2.setValue("four");
1929       fail("UnsupportedOperationException expected");
1930     } catch (UnsupportedOperationException expected) {
1931     }
1932   }
1933 
1934   @GwtIncompatible("NavigableMap")
1935   void ensureNotDirectlyModifiable(NavigableMap<Integer, String> unmod) {
1936     try {
1937       unmod.put(4, "four");
1938       fail("UnsupportedOperationException expected");
1939     } catch (UnsupportedOperationException expected) {
1940     }
1941     try {
1942       unmod.putAll(Collections.singletonMap(4, "four"));
1943       fail("UnsupportedOperationException expected");
1944     } catch (UnsupportedOperationException expected) {
1945     }
1946     try {
1947       unmod.remove("four");
1948       fail("UnsupportedOperationException expected");
1949     } catch (UnsupportedOperationException expected) {
1950     }
1951     try {
1952       unmod.pollFirstEntry();
1953       fail("UnsupportedOperationException expected");
1954     } catch (UnsupportedOperationException expected) {
1955     }
1956     try {
1957       unmod.pollLastEntry();
1958       fail("UnsupportedOperationException expected");
1959     } catch (UnsupportedOperationException expected) {
1960     }
1961   }
1962 }