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