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