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