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