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
17package com.google.common.collect;
18
19import static com.google.common.collect.Maps.transformEntries;
20import static com.google.common.collect.Maps.transformValues;
21import static com.google.common.collect.Maps.unmodifiableNavigableMap;
22import static com.google.common.collect.testing.Helpers.mapEntry;
23import static com.google.common.truth.Truth.assertThat;
24import static java.util.Arrays.asList;
25
26import com.google.common.annotations.GwtCompatible;
27import com.google.common.annotations.GwtIncompatible;
28import com.google.common.base.Converter;
29import com.google.common.base.Equivalence;
30import com.google.common.base.Function;
31import com.google.common.base.Functions;
32import com.google.common.base.Predicate;
33import com.google.common.base.Predicates;
34import com.google.common.collect.Maps.EntryTransformer;
35import com.google.common.collect.Maps.ValueDifferenceImpl;
36import com.google.common.collect.SetsTest.Derived;
37import com.google.common.testing.EqualsTester;
38import com.google.common.testing.NullPointerTester;
39import com.google.common.testing.SerializableTester;
40
41import junit.framework.TestCase;
42
43import java.io.IOException;
44import java.io.StringReader;
45import java.lang.reflect.Field;
46import java.util.Arrays;
47import java.util.Collection;
48import java.util.Collections;
49import java.util.Comparator;
50import java.util.EnumMap;
51import java.util.Enumeration;
52import java.util.HashMap;
53import java.util.IdentityHashMap;
54import java.util.Iterator;
55import java.util.LinkedHashMap;
56import java.util.List;
57import java.util.Map;
58import java.util.Map.Entry;
59import java.util.NavigableMap;
60import java.util.NavigableSet;
61import java.util.Properties;
62import java.util.Set;
63import java.util.SortedMap;
64import java.util.SortedSet;
65import java.util.TreeMap;
66import 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)
76public 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}
1963