001/* 002 * Copyright (C) 2010 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect.testing; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.GwtIncompatible; 022import com.google.errorprone.annotations.CanIgnoreReturnValue; 023import java.io.Serializable; 024import java.util.AbstractSet; 025import java.util.Collection; 026import java.util.Comparator; 027import java.util.Iterator; 028import java.util.Map; 029import java.util.NavigableMap; 030import java.util.NavigableSet; 031import java.util.Set; 032import java.util.SortedMap; 033import java.util.TreeMap; 034import org.jspecify.annotations.Nullable; 035 036/** 037 * A wrapper around {@code TreeMap} that aggressively checks to see if keys are mutually comparable. 038 * This implementation passes the navigable map test suites. 039 * 040 * @author Louis Wasserman 041 */ 042@GwtIncompatible 043public final class SafeTreeMap<K, V> implements Serializable, NavigableMap<K, V> { 044 @SuppressWarnings("unchecked") 045 private static final Comparator<Object> NATURAL_ORDER = 046 (o1, o2) -> ((Comparable<Object>) o1).compareTo(o2); 047 048 private final NavigableMap<K, V> delegate; 049 050 public SafeTreeMap() { 051 this(new TreeMap<K, V>()); 052 } 053 054 public SafeTreeMap(Comparator<? super K> comparator) { 055 this(new TreeMap<K, V>(comparator)); 056 } 057 058 public SafeTreeMap(Map<? extends K, ? extends V> map) { 059 this(new TreeMap<K, V>(map)); 060 } 061 062 public SafeTreeMap(SortedMap<K, ? extends V> map) { 063 this(new TreeMap<K, V>(map)); 064 } 065 066 private SafeTreeMap(NavigableMap<K, V> delegate) { 067 this.delegate = checkNotNull(delegate); 068 for (K k : keySet()) { 069 checkValid(k); 070 } 071 } 072 073 @Override 074 public @Nullable Entry<K, V> ceilingEntry(K key) { 075 return delegate.ceilingEntry(checkValid(key)); 076 } 077 078 @Override 079 public @Nullable K ceilingKey(K key) { 080 return delegate.ceilingKey(checkValid(key)); 081 } 082 083 @Override 084 public void clear() { 085 delegate.clear(); 086 } 087 088 @Override 089 public Comparator<? super K> comparator() { 090 Comparator<? super K> comparator = delegate.comparator(); 091 if (comparator != null) { 092 return comparator; 093 } 094 return NATURAL_ORDER; 095 } 096 097 @Override 098 public boolean containsKey(@Nullable Object key) { 099 try { 100 return delegate.containsKey(checkValid(key)); 101 } catch (NullPointerException | ClassCastException e) { 102 return false; 103 } 104 } 105 106 @Override 107 public boolean containsValue(@Nullable Object value) { 108 return delegate.containsValue(value); 109 } 110 111 @Override 112 public NavigableSet<K> descendingKeySet() { 113 return delegate.descendingKeySet(); 114 } 115 116 @Override 117 public NavigableMap<K, V> descendingMap() { 118 return new SafeTreeMap<>(delegate.descendingMap()); 119 } 120 121 @Override 122 public Set<Entry<K, V>> entrySet() { 123 return new AbstractSet<Entry<K, V>>() { 124 private Set<Entry<K, V>> delegate() { 125 return delegate.entrySet(); 126 } 127 128 @Override 129 public boolean contains(@Nullable Object object) { 130 try { 131 return delegate().contains(object); 132 } catch (NullPointerException | ClassCastException e) { 133 return false; 134 } 135 } 136 137 @Override 138 public Iterator<Entry<K, V>> iterator() { 139 return delegate().iterator(); 140 } 141 142 @Override 143 public int size() { 144 return delegate().size(); 145 } 146 147 @Override 148 public boolean remove(@Nullable Object o) { 149 return delegate().remove(o); 150 } 151 152 @Override 153 public void clear() { 154 delegate().clear(); 155 } 156 }; 157 } 158 159 @Override 160 public @Nullable Entry<K, V> firstEntry() { 161 return delegate.firstEntry(); 162 } 163 164 @Override 165 public K firstKey() { 166 return delegate.firstKey(); 167 } 168 169 @Override 170 public @Nullable Entry<K, V> floorEntry(K key) { 171 return delegate.floorEntry(checkValid(key)); 172 } 173 174 @Override 175 public @Nullable K floorKey(K key) { 176 return delegate.floorKey(checkValid(key)); 177 } 178 179 @Override 180 public @Nullable V get(@Nullable Object key) { 181 return delegate.get(checkValid(key)); 182 } 183 184 @Override 185 public SortedMap<K, V> headMap(K toKey) { 186 return headMap(toKey, false); 187 } 188 189 @Override 190 public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { 191 return new SafeTreeMap<>(delegate.headMap(checkValid(toKey), inclusive)); 192 } 193 194 @Override 195 public @Nullable Entry<K, V> higherEntry(K key) { 196 return delegate.higherEntry(checkValid(key)); 197 } 198 199 @Override 200 public @Nullable K higherKey(K key) { 201 return delegate.higherKey(checkValid(key)); 202 } 203 204 @Override 205 public boolean isEmpty() { 206 return delegate.isEmpty(); 207 } 208 209 @Override 210 public NavigableSet<K> keySet() { 211 return navigableKeySet(); 212 } 213 214 @Override 215 public @Nullable Entry<K, V> lastEntry() { 216 return delegate.lastEntry(); 217 } 218 219 @Override 220 public K lastKey() { 221 return delegate.lastKey(); 222 } 223 224 @Override 225 public @Nullable Entry<K, V> lowerEntry(K key) { 226 return delegate.lowerEntry(checkValid(key)); 227 } 228 229 @Override 230 public @Nullable K lowerKey(K key) { 231 return delegate.lowerKey(checkValid(key)); 232 } 233 234 @Override 235 public NavigableSet<K> navigableKeySet() { 236 return delegate.navigableKeySet(); 237 } 238 239 @Override 240 public @Nullable Entry<K, V> pollFirstEntry() { 241 return delegate.pollFirstEntry(); 242 } 243 244 @Override 245 public @Nullable Entry<K, V> pollLastEntry() { 246 return delegate.pollLastEntry(); 247 } 248 249 @Override 250 public @Nullable V put(K key, V value) { 251 return delegate.put(checkValid(key), value); 252 } 253 254 @Override 255 public void putAll(Map<? extends K, ? extends V> map) { 256 for (K key : map.keySet()) { 257 checkValid(key); 258 } 259 delegate.putAll(map); 260 } 261 262 @Override 263 public @Nullable V remove(@Nullable Object key) { 264 return delegate.remove(checkValid(key)); 265 } 266 267 @Override 268 public int size() { 269 return delegate.size(); 270 } 271 272 @Override 273 public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { 274 return new SafeTreeMap<>( 275 delegate.subMap(checkValid(fromKey), fromInclusive, checkValid(toKey), toInclusive)); 276 } 277 278 @Override 279 public SortedMap<K, V> subMap(K fromKey, K toKey) { 280 return subMap(fromKey, true, toKey, false); 281 } 282 283 @Override 284 public SortedMap<K, V> tailMap(K fromKey) { 285 return tailMap(fromKey, true); 286 } 287 288 @Override 289 public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { 290 return new SafeTreeMap<>(delegate.tailMap(checkValid(fromKey), inclusive)); 291 } 292 293 @Override 294 public Collection<V> values() { 295 return delegate.values(); 296 } 297 298 @CanIgnoreReturnValue 299 private <T> T checkValid(T t) { 300 // a ClassCastException is what's supposed to happen! 301 @SuppressWarnings("unchecked") 302 K k = (K) t; 303 int unused = comparator().compare(k, k); 304 return t; 305 } 306 307 @Override 308 public boolean equals(@Nullable Object obj) { 309 return delegate.equals(obj); 310 } 311 312 @Override 313 public int hashCode() { 314 return delegate.hashCode(); 315 } 316 317 @Override 318 public String toString() { 319 return delegate.toString(); 320 } 321 322 private static final long serialVersionUID = 0L; 323}