1 /* 2 * Copyright (C) 2010 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.testing; 18 19 import com.google.common.annotations.GwtIncompatible; 20 import com.google.errorprone.annotations.CanIgnoreReturnValue; 21 import java.io.Serializable; 22 import java.util.AbstractSet; 23 import java.util.Collection; 24 import java.util.Comparator; 25 import java.util.Iterator; 26 import java.util.Map; 27 import java.util.NavigableMap; 28 import java.util.NavigableSet; 29 import java.util.Set; 30 import java.util.SortedMap; 31 import java.util.TreeMap; 32 import org.checkerframework.checker.nullness.qual.Nullable; 33 34 /** 35 * A wrapper around {@code TreeMap} that aggressively checks to see if keys are mutually comparable. 36 * This implementation passes the navigable map test suites. 37 * 38 * @author Louis Wasserman 39 */ 40 @GwtIncompatible 41 public final class SafeTreeMap<K, V> implements Serializable, NavigableMap<K, V> { 42 @SuppressWarnings("unchecked") 43 private static final Comparator<Object> NATURAL_ORDER = 44 new Comparator<Object>() { 45 @Override 46 public int compare(Object o1, Object o2) { 47 return ((Comparable<Object>) o1).compareTo(o2); 48 } 49 }; 50 51 private final NavigableMap<K, V> delegate; 52 SafeTreeMap()53 public SafeTreeMap() { 54 this(new TreeMap<K, V>()); 55 } 56 SafeTreeMap(Comparator<? super K> comparator)57 public SafeTreeMap(Comparator<? super K> comparator) { 58 this(new TreeMap<K, V>(comparator)); 59 } 60 SafeTreeMap(Map<? extends K, ? extends V> map)61 public SafeTreeMap(Map<? extends K, ? extends V> map) { 62 this(new TreeMap<K, V>(map)); 63 } 64 SafeTreeMap(SortedMap<K, ? extends V> map)65 public SafeTreeMap(SortedMap<K, ? extends V> map) { 66 this(new TreeMap<K, V>(map)); 67 } 68 SafeTreeMap(NavigableMap<K, V> delegate)69 private SafeTreeMap(NavigableMap<K, V> delegate) { 70 this.delegate = delegate; 71 if (delegate == null) { 72 throw new NullPointerException(); 73 } 74 for (K k : keySet()) { 75 checkValid(k); 76 } 77 } 78 79 @Override ceilingEntry(K key)80 public @Nullable Entry<K, V> ceilingEntry(K key) { 81 return delegate.ceilingEntry(checkValid(key)); 82 } 83 84 @Override ceilingKey(K key)85 public @Nullable K ceilingKey(K key) { 86 return delegate.ceilingKey(checkValid(key)); 87 } 88 89 @Override clear()90 public void clear() { 91 delegate.clear(); 92 } 93 94 @SuppressWarnings("unchecked") 95 @Override comparator()96 public Comparator<? super K> comparator() { 97 Comparator<? super K> comparator = delegate.comparator(); 98 if (comparator == null) { 99 comparator = (Comparator<? super K>) NATURAL_ORDER; 100 } 101 return comparator; 102 } 103 104 @Override containsKey(Object key)105 public boolean containsKey(Object key) { 106 try { 107 return delegate.containsKey(checkValid(key)); 108 } catch (NullPointerException | ClassCastException e) { 109 return false; 110 } 111 } 112 113 @Override containsValue(Object value)114 public boolean containsValue(Object value) { 115 return delegate.containsValue(value); 116 } 117 118 @Override descendingKeySet()119 public NavigableSet<K> descendingKeySet() { 120 return delegate.descendingKeySet(); 121 } 122 123 @Override descendingMap()124 public NavigableMap<K, V> descendingMap() { 125 return new SafeTreeMap<>(delegate.descendingMap()); 126 } 127 128 @Override entrySet()129 public Set<Entry<K, V>> entrySet() { 130 return new AbstractSet<Entry<K, V>>() { 131 private Set<Entry<K, V>> delegate() { 132 return delegate.entrySet(); 133 } 134 135 @Override 136 public boolean contains(Object object) { 137 try { 138 return delegate().contains(object); 139 } catch (NullPointerException | ClassCastException e) { 140 return false; 141 } 142 } 143 144 @Override 145 public Iterator<Entry<K, V>> iterator() { 146 return delegate().iterator(); 147 } 148 149 @Override 150 public int size() { 151 return delegate().size(); 152 } 153 154 @Override 155 public boolean remove(Object o) { 156 return delegate().remove(o); 157 } 158 159 @Override 160 public void clear() { 161 delegate().clear(); 162 } 163 }; 164 } 165 166 @Override 167 public @Nullable Entry<K, V> firstEntry() { 168 return delegate.firstEntry(); 169 } 170 171 @Override 172 public K firstKey() { 173 return delegate.firstKey(); 174 } 175 176 @Override 177 public @Nullable Entry<K, V> floorEntry(K key) { 178 return delegate.floorEntry(checkValid(key)); 179 } 180 181 @Override 182 public @Nullable K floorKey(K key) { 183 return delegate.floorKey(checkValid(key)); 184 } 185 186 @Override 187 public @Nullable V get(Object key) { 188 return delegate.get(checkValid(key)); 189 } 190 191 @Override 192 public SortedMap<K, V> headMap(K toKey) { 193 return headMap(toKey, false); 194 } 195 196 @Override 197 public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { 198 return new SafeTreeMap<>(delegate.headMap(checkValid(toKey), inclusive)); 199 } 200 201 @Override 202 public @Nullable Entry<K, V> higherEntry(K key) { 203 return delegate.higherEntry(checkValid(key)); 204 } 205 206 @Override 207 public @Nullable K higherKey(K key) { 208 return delegate.higherKey(checkValid(key)); 209 } 210 211 @Override 212 public boolean isEmpty() { 213 return delegate.isEmpty(); 214 } 215 216 @Override 217 public NavigableSet<K> keySet() { 218 return navigableKeySet(); 219 } 220 221 @Override 222 public @Nullable Entry<K, V> lastEntry() { 223 return delegate.lastEntry(); 224 } 225 226 @Override 227 public K lastKey() { 228 return delegate.lastKey(); 229 } 230 231 @Override 232 public @Nullable Entry<K, V> lowerEntry(K key) { 233 return delegate.lowerEntry(checkValid(key)); 234 } 235 236 @Override 237 public @Nullable K lowerKey(K key) { 238 return delegate.lowerKey(checkValid(key)); 239 } 240 241 @Override 242 public NavigableSet<K> navigableKeySet() { 243 return delegate.navigableKeySet(); 244 } 245 246 @Override 247 public @Nullable Entry<K, V> pollFirstEntry() { 248 return delegate.pollFirstEntry(); 249 } 250 251 @Override 252 public @Nullable Entry<K, V> pollLastEntry() { 253 return delegate.pollLastEntry(); 254 } 255 256 @Override 257 public @Nullable V put(K key, V value) { 258 return delegate.put(checkValid(key), value); 259 } 260 261 @Override 262 public void putAll(Map<? extends K, ? extends V> map) { 263 for (K key : map.keySet()) { 264 checkValid(key); 265 } 266 delegate.putAll(map); 267 } 268 269 @Override 270 public @Nullable V remove(Object key) { 271 return delegate.remove(checkValid(key)); 272 } 273 274 @Override 275 public int size() { 276 return delegate.size(); 277 } 278 279 @Override 280 public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { 281 return new SafeTreeMap<>( 282 delegate.subMap(checkValid(fromKey), fromInclusive, checkValid(toKey), toInclusive)); 283 } 284 285 @Override 286 public SortedMap<K, V> subMap(K fromKey, K toKey) { 287 return subMap(fromKey, true, toKey, false); 288 } 289 290 @Override 291 public SortedMap<K, V> tailMap(K fromKey) { 292 return tailMap(fromKey, true); 293 } 294 295 @Override 296 public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { 297 return new SafeTreeMap<>(delegate.tailMap(checkValid(fromKey), inclusive)); 298 } 299 300 @Override 301 public Collection<V> values() { 302 return delegate.values(); 303 } 304 305 @CanIgnoreReturnValue 306 private <T> T checkValid(T t) { 307 // a ClassCastException is what's supposed to happen! 308 @SuppressWarnings("unchecked") 309 K k = (K) t; 310 int unused = comparator().compare(k, k); 311 return t; 312 } 313 314 @Override 315 public boolean equals(@Nullable Object obj) { 316 return delegate.equals(obj); 317 } 318 319 @Override 320 public int hashCode() { 321 return delegate.hashCode(); 322 } 323 324 @Override 325 public String toString() { 326 return delegate.toString(); 327 } 328 329 private static final long serialVersionUID = 0L; 330 } 331