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 static com.google.common.collect.Maps.immutableEntry; 20 21 import com.google.common.base.Function; 22 import com.google.common.collect.testing.NavigableMapTestSuiteBuilder; 23 import com.google.common.collect.testing.SafeTreeMap; 24 import com.google.common.collect.testing.TestStringSortedMapGenerator; 25 import com.google.common.collect.testing.features.CollectionFeature; 26 import com.google.common.collect.testing.features.CollectionSize; 27 import com.google.common.collect.testing.features.MapFeature; 28 import com.google.common.collect.testing.testers.MapEntrySetTester; 29 import com.google.common.testing.EqualsTester; 30 import com.google.common.testing.ForwardingWrapperTester; 31 import java.util.Collection; 32 import java.util.Iterator; 33 import java.util.Map; 34 import java.util.Map.Entry; 35 import java.util.NavigableMap; 36 import java.util.NavigableSet; 37 import java.util.Set; 38 import java.util.SortedMap; 39 import junit.framework.Test; 40 import junit.framework.TestCase; 41 import junit.framework.TestSuite; 42 import org.checkerframework.checker.nullness.qual.Nullable; 43 44 /** 45 * Tests for {@code ForwardingNavigableMap}. 46 * 47 * @author Robert Konigsberg 48 * @author Louis Wasserman 49 */ 50 public class ForwardingNavigableMapTest extends TestCase { 51 static class StandardImplForwardingNavigableMap<K, V> extends ForwardingNavigableMap<K, V> { 52 private final NavigableMap<K, V> backingMap; 53 StandardImplForwardingNavigableMap(NavigableMap<K, V> backingMap)54 StandardImplForwardingNavigableMap(NavigableMap<K, V> backingMap) { 55 this.backingMap = backingMap; 56 } 57 58 @Override delegate()59 protected NavigableMap<K, V> delegate() { 60 return backingMap; 61 } 62 63 @Override containsKey(Object key)64 public boolean containsKey(Object key) { 65 return standardContainsKey(key); 66 } 67 68 @Override containsValue(Object value)69 public boolean containsValue(Object value) { 70 return standardContainsValue(value); 71 } 72 73 @Override putAll(Map<? extends K, ? extends V> map)74 public void putAll(Map<? extends K, ? extends V> map) { 75 standardPutAll(map); 76 } 77 78 @Override remove(Object object)79 public @Nullable V remove(Object object) { 80 return standardRemove(object); 81 } 82 83 @Override equals(@ullable Object object)84 public boolean equals(@Nullable Object object) { 85 return standardEquals(object); 86 } 87 88 @Override hashCode()89 public int hashCode() { 90 return standardHashCode(); 91 } 92 93 @Override keySet()94 public Set<K> keySet() { 95 /* 96 * We can't use StandardKeySet, as NavigableMapTestSuiteBuilder assumes that our keySet is a 97 * NavigableSet. We test StandardKeySet in the superclass, so it's still covered. 98 */ 99 return navigableKeySet(); 100 } 101 102 @Override values()103 public Collection<V> values() { 104 return new StandardValues(); 105 } 106 107 @Override toString()108 public String toString() { 109 return standardToString(); 110 } 111 112 @Override entrySet()113 public Set<Entry<K, V>> entrySet() { 114 return new StandardEntrySet() { 115 @Override 116 public Iterator<Entry<K, V>> iterator() { 117 return backingMap.entrySet().iterator(); 118 } 119 }; 120 } 121 122 @Override clear()123 public void clear() { 124 standardClear(); 125 } 126 127 @Override isEmpty()128 public boolean isEmpty() { 129 return standardIsEmpty(); 130 } 131 132 @Override subMap(K fromKey, K toKey)133 public SortedMap<K, V> subMap(K fromKey, K toKey) { 134 return standardSubMap(fromKey, toKey); 135 } 136 137 @Override lowerEntry(K key)138 public @Nullable Entry<K, V> lowerEntry(K key) { 139 return standardLowerEntry(key); 140 } 141 142 @Override lowerKey(K key)143 public @Nullable K lowerKey(K key) { 144 return standardLowerKey(key); 145 } 146 147 @Override floorEntry(K key)148 public @Nullable Entry<K, V> floorEntry(K key) { 149 return standardFloorEntry(key); 150 } 151 152 @Override floorKey(K key)153 public @Nullable K floorKey(K key) { 154 return standardFloorKey(key); 155 } 156 157 @Override ceilingEntry(K key)158 public @Nullable Entry<K, V> ceilingEntry(K key) { 159 return standardCeilingEntry(key); 160 } 161 162 @Override ceilingKey(K key)163 public @Nullable K ceilingKey(K key) { 164 return standardCeilingKey(key); 165 } 166 167 @Override higherEntry(K key)168 public @Nullable Entry<K, V> higherEntry(K key) { 169 return standardHigherEntry(key); 170 } 171 172 @Override higherKey(K key)173 public @Nullable K higherKey(K key) { 174 return standardHigherKey(key); 175 } 176 177 @Override firstEntry()178 public @Nullable Entry<K, V> firstEntry() { 179 return standardFirstEntry(); 180 } 181 182 /* 183 * We can't override lastEntry to delegate to standardLastEntry, as it would create an infinite 184 * loop. Instead, we test standardLastEntry manually below. 185 */ 186 187 @Override pollFirstEntry()188 public @Nullable Entry<K, V> pollFirstEntry() { 189 return standardPollFirstEntry(); 190 } 191 192 @Override pollLastEntry()193 public @Nullable Entry<K, V> pollLastEntry() { 194 return standardPollLastEntry(); 195 } 196 197 @Override descendingMap()198 public NavigableMap<K, V> descendingMap() { 199 return new StandardDescendingMap(); 200 } 201 202 @Override navigableKeySet()203 public NavigableSet<K> navigableKeySet() { 204 return new StandardNavigableKeySet(); 205 } 206 207 @Override descendingKeySet()208 public NavigableSet<K> descendingKeySet() { 209 return standardDescendingKeySet(); 210 } 211 212 @Override firstKey()213 public K firstKey() { 214 return standardFirstKey(); 215 } 216 217 @Override headMap(K toKey)218 public SortedMap<K, V> headMap(K toKey) { 219 return standardHeadMap(toKey); 220 } 221 222 @Override lastKey()223 public K lastKey() { 224 return standardLastKey(); 225 } 226 227 @Override tailMap(K fromKey)228 public SortedMap<K, V> tailMap(K fromKey) { 229 return standardTailMap(fromKey); 230 } 231 } 232 233 static class StandardLastEntryForwardingNavigableMap<K, V> extends ForwardingNavigableMap<K, V> { 234 private final NavigableMap<K, V> backingMap; 235 236 StandardLastEntryForwardingNavigableMap(NavigableMap<K, V> backingMap) { 237 this.backingMap = backingMap; 238 } 239 240 @Override 241 protected NavigableMap<K, V> delegate() { 242 return backingMap; 243 } 244 245 @Override 246 public @Nullable Entry<K, V> lastEntry() { 247 return standardLastEntry(); 248 } 249 } 250 251 public static Test suite() { 252 TestSuite suite = new TestSuite(); 253 254 suite.addTestSuite(ForwardingNavigableMapTest.class); 255 suite.addTest( 256 NavigableMapTestSuiteBuilder.using( 257 new TestStringSortedMapGenerator() { 258 @Override 259 protected SortedMap<String, String> create(Entry<String, String>[] entries) { 260 NavigableMap<String, String> map = new SafeTreeMap<>(); 261 for (Entry<String, String> entry : entries) { 262 map.put(entry.getKey(), entry.getValue()); 263 } 264 return new StandardImplForwardingNavigableMap<>(map); 265 } 266 }) 267 .named( 268 "ForwardingNavigableMap[SafeTreeMap] with no comparator and standard " 269 + "implementations") 270 .withFeatures( 271 CollectionSize.ANY, 272 CollectionFeature.KNOWN_ORDER, 273 MapFeature.ALLOWS_NULL_VALUES, 274 CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 275 MapFeature.GENERAL_PURPOSE) 276 /* 277 * StandardDescendingMap uses lowerEntry(), and TreeMap.lowerEntry() deliberately 278 * produces immutable entries. 279 * 280 * TODO(cpovirk): Consider making StandardDescendingMap return a ForwardingEntry that 281 * supports setValue(). 282 */ 283 .suppressing( 284 MapEntrySetTester.getSetValueMethod(), 285 MapEntrySetTester.getSetValueWithNullValuesAbsentMethod(), 286 MapEntrySetTester.getSetValueWithNullValuesPresentMethod()) 287 .createTestSuite()); 288 // TODO(lowasser): add forwarding-to-ImmutableSortedMap test 289 return suite; 290 } 291 292 public void testStandardLastEntry() { 293 NavigableMap<String, Integer> forwarding = 294 new StandardLastEntryForwardingNavigableMap<>(new SafeTreeMap<String, Integer>()); 295 assertNull(forwarding.lastEntry()); 296 forwarding.put("b", 2); 297 assertEquals(immutableEntry("b", 2), forwarding.lastEntry()); 298 forwarding.put("c", 3); 299 assertEquals(immutableEntry("c", 3), forwarding.lastEntry()); 300 forwarding.put("a", 1); 301 assertEquals(immutableEntry("c", 3), forwarding.lastEntry()); 302 forwarding.remove("c"); 303 assertEquals(immutableEntry("b", 2), forwarding.lastEntry()); 304 } 305 306 @SuppressWarnings({"rawtypes", "unchecked"}) 307 public void testForwarding() { 308 new ForwardingWrapperTester() 309 .testForwarding( 310 NavigableMap.class, 311 new Function<NavigableMap, NavigableMap>() { 312 @Override 313 public NavigableMap apply(NavigableMap delegate) { 314 return wrap(delegate); 315 } 316 }); 317 } 318 319 public void testEquals() { 320 NavigableMap<Integer, String> map1 = ImmutableSortedMap.of(1, "one"); 321 NavigableMap<Integer, String> map2 = ImmutableSortedMap.of(2, "two"); 322 new EqualsTester() 323 .addEqualityGroup(map1, wrap(map1), wrap(map1)) 324 .addEqualityGroup(map2, wrap(map2)) 325 .testEquals(); 326 } 327 328 private static <K, V> NavigableMap<K, V> wrap(final NavigableMap<K, V> delegate) { 329 return new ForwardingNavigableMap<K, V>() { 330 @Override 331 protected NavigableMap<K, V> delegate() { 332 return delegate; 333 } 334 }; 335 } 336 } 337