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}