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