001/*
002 * Copyright (C) 2012 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;
018
019import static com.google.common.base.Preconditions.checkArgument;
020import static com.google.common.base.Preconditions.checkNotNull;
021import static com.google.common.base.Predicates.compose;
022import static com.google.common.base.Predicates.in;
023import static com.google.common.base.Predicates.not;
024
025import com.google.common.annotations.Beta;
026import com.google.common.annotations.GwtIncompatible;
027import com.google.common.base.MoreObjects;
028import com.google.common.base.Predicate;
029import com.google.common.collect.Maps.IteratorBasedAbstractMap;
030
031import java.util.AbstractMap;
032import java.util.Collection;
033import java.util.Collections;
034import java.util.Iterator;
035import java.util.List;
036import java.util.Map;
037import java.util.Map.Entry;
038import java.util.NavigableMap;
039import java.util.NoSuchElementException;
040import java.util.Set;
041
042import javax.annotation.Nullable;
043
044/**
045 * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting
046 * all optional operations.
047 *
048 * <p>Like all {@code RangeMap} implementations, this supports neither null
049 * keys nor null values.
050 *
051 * @author Louis Wasserman
052 * @since 14.0
053 */
054@Beta
055@GwtIncompatible("NavigableMap")
056public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K, V> {
057
058  private final NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound;
059
060  public static <K extends Comparable, V> TreeRangeMap<K, V> create() {
061    return new TreeRangeMap<K, V>();
062  }
063
064  private TreeRangeMap() {
065    this.entriesByLowerBound = Maps.newTreeMap();
066  }
067
068  private static final class RangeMapEntry<K extends Comparable, V>
069      extends AbstractMapEntry<Range<K>, V> {
070    private final Range<K> range;
071    private final V value;
072
073    RangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) {
074      this(Range.create(lowerBound, upperBound), value);
075    }
076
077    RangeMapEntry(Range<K> range, V value) {
078      this.range = range;
079      this.value = value;
080    }
081
082    @Override
083    public Range<K> getKey() {
084      return range;
085    }
086
087    @Override
088    public V getValue() {
089      return value;
090    }
091
092    public boolean contains(K value) {
093      return range.contains(value);
094    }
095
096    Cut<K> getLowerBound() {
097      return range.lowerBound;
098    }
099
100    Cut<K> getUpperBound() {
101      return range.upperBound;
102    }
103  }
104
105  @Override
106  @Nullable
107  public V get(K key) {
108    Entry<Range<K>, V> entry = getEntry(key);
109    return (entry == null) ? null : entry.getValue();
110  }
111
112  @Override
113  @Nullable
114  public Entry<Range<K>, V> getEntry(K key) {
115    Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntry =
116        entriesByLowerBound.floorEntry(Cut.belowValue(key));
117    if (mapEntry != null && mapEntry.getValue().contains(key)) {
118      return mapEntry.getValue();
119    } else {
120      return null;
121    }
122  }
123
124  @Override
125  public void put(Range<K> range, V value) {
126    if (!range.isEmpty()) {
127      checkNotNull(value);
128      remove(range);
129      entriesByLowerBound.put(range.lowerBound, new RangeMapEntry<K, V>(range, value));
130    }
131  }
132
133  @Override
134  public void putAll(RangeMap<K, V> rangeMap) {
135    for (Map.Entry<Range<K>, V> entry : rangeMap.asMapOfRanges().entrySet()) {
136      put(entry.getKey(), entry.getValue());
137    }
138  }
139
140  @Override
141  public void clear() {
142    entriesByLowerBound.clear();
143  }
144
145  @Override
146  public Range<K> span() {
147    Entry<Cut<K>, RangeMapEntry<K, V>> firstEntry = entriesByLowerBound.firstEntry();
148    Entry<Cut<K>, RangeMapEntry<K, V>> lastEntry = entriesByLowerBound.lastEntry();
149    if (firstEntry == null) {
150      throw new NoSuchElementException();
151    }
152    return Range.create(
153        firstEntry.getValue().getKey().lowerBound, lastEntry.getValue().getKey().upperBound);
154  }
155
156  private void putRangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) {
157    entriesByLowerBound.put(lowerBound, new RangeMapEntry<K, V>(lowerBound, upperBound, value));
158  }
159
160  @Override
161  public void remove(Range<K> rangeToRemove) {
162    if (rangeToRemove.isEmpty()) {
163      return;
164    }
165
166    /*
167     * The comments for this method will use [ ] to indicate the bounds of rangeToRemove and ( ) to
168     * indicate the bounds of ranges in the range map.
169     */
170    Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryBelowToTruncate =
171        entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound);
172    if (mapEntryBelowToTruncate != null) {
173      // we know ( [
174      RangeMapEntry<K, V> rangeMapEntry = mapEntryBelowToTruncate.getValue();
175      if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) {
176        // we know ( [ )
177        if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
178          // we know ( [ ] ), so insert the range ] ) back into the map --
179          // it's being split apart
180          putRangeMapEntry(
181              rangeToRemove.upperBound,
182              rangeMapEntry.getUpperBound(),
183              mapEntryBelowToTruncate.getValue().getValue());
184        }
185        // overwrite mapEntryToTruncateBelow with a truncated range
186        putRangeMapEntry(
187            rangeMapEntry.getLowerBound(),
188            rangeToRemove.lowerBound,
189            mapEntryBelowToTruncate.getValue().getValue());
190      }
191    }
192
193    Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryAboveToTruncate =
194        entriesByLowerBound.lowerEntry(rangeToRemove.upperBound);
195    if (mapEntryAboveToTruncate != null) {
196      // we know ( ]
197      RangeMapEntry<K, V> rangeMapEntry = mapEntryAboveToTruncate.getValue();
198      if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
199        // we know ( ] ), and since we dealt with truncating below already,
200        // we know [ ( ] )
201        putRangeMapEntry(
202            rangeToRemove.upperBound,
203            rangeMapEntry.getUpperBound(),
204            mapEntryAboveToTruncate.getValue().getValue());
205        entriesByLowerBound.remove(rangeToRemove.lowerBound);
206      }
207    }
208    entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear();
209  }
210
211  @Override
212  public Map<Range<K>, V> asMapOfRanges() {
213    return new AsMapOfRanges(entriesByLowerBound.values());
214  }
215
216  @Override
217  public Map<Range<K>, V> asDescendingMapOfRanges() {
218    return new AsMapOfRanges(entriesByLowerBound.descendingMap().values());
219  }
220
221  private final class AsMapOfRanges extends IteratorBasedAbstractMap<Range<K>, V> {
222
223    final Iterable<Entry<Range<K>, V>> entryIterable;
224
225    @SuppressWarnings("unchecked") // it's safe to upcast iterables
226    AsMapOfRanges(Iterable<RangeMapEntry<K, V>> entryIterable) {
227      this.entryIterable = (Iterable) entryIterable;
228    }
229
230    @Override
231    public boolean containsKey(@Nullable Object key) {
232      return get(key) != null;
233    }
234
235    @Override
236    public V get(@Nullable Object key) {
237      if (key instanceof Range) {
238        Range<?> range = (Range<?>) key;
239        RangeMapEntry<K, V> rangeMapEntry = entriesByLowerBound.get(range.lowerBound);
240        if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) {
241          return rangeMapEntry.getValue();
242        }
243      }
244      return null;
245    }
246
247    @Override
248    public int size() {
249      return entriesByLowerBound.size();
250    }
251
252    @Override
253    Iterator<Entry<Range<K>, V>> entryIterator() {
254      return entryIterable.iterator();
255    }
256  }
257
258  @Override
259  public RangeMap<K, V> subRangeMap(Range<K> subRange) {
260    if (subRange.equals(Range.all())) {
261      return this;
262    } else {
263      return new SubRangeMap(subRange);
264    }
265  }
266
267  @SuppressWarnings("unchecked")
268  private RangeMap<K, V> emptySubRangeMap() {
269    return EMPTY_SUB_RANGE_MAP;
270  }
271
272  private static final RangeMap EMPTY_SUB_RANGE_MAP =
273      new RangeMap() {
274        @Override
275        @Nullable
276        public Object get(Comparable key) {
277          return null;
278        }
279
280        @Override
281        @Nullable
282        public Entry<Range, Object> getEntry(Comparable key) {
283          return null;
284        }
285
286        @Override
287        public Range span() {
288          throw new NoSuchElementException();
289        }
290
291        @Override
292        public void put(Range range, Object value) {
293          checkNotNull(range);
294          throw new IllegalArgumentException(
295              "Cannot insert range " + range + " into an empty subRangeMap");
296        }
297
298        @Override
299        public void putAll(RangeMap rangeMap) {
300          if (!rangeMap.asMapOfRanges().isEmpty()) {
301            throw new IllegalArgumentException(
302                "Cannot putAll(nonEmptyRangeMap) into an empty " + "subRangeMap");
303          }
304        }
305
306        @Override
307        public void clear() {}
308
309        @Override
310        public void remove(Range range) {
311          checkNotNull(range);
312        }
313
314        @Override
315        public Map<Range, Object> asMapOfRanges() {
316          return Collections.emptyMap();
317        }
318
319        @Override
320        public Map<Range, Object> asDescendingMapOfRanges() {
321          return Collections.emptyMap();
322        }
323
324        @Override
325        public RangeMap subRangeMap(Range range) {
326          checkNotNull(range);
327          return this;
328        }
329      };
330
331  private class SubRangeMap implements RangeMap<K, V> {
332
333    private final Range<K> subRange;
334
335    SubRangeMap(Range<K> subRange) {
336      this.subRange = subRange;
337    }
338
339    @Override
340    @Nullable
341    public V get(K key) {
342      return subRange.contains(key) ? TreeRangeMap.this.get(key) : null;
343    }
344
345    @Override
346    @Nullable
347    public Entry<Range<K>, V> getEntry(K key) {
348      if (subRange.contains(key)) {
349        Entry<Range<K>, V> entry = TreeRangeMap.this.getEntry(key);
350        if (entry != null) {
351          return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
352        }
353      }
354      return null;
355    }
356
357    @Override
358    public Range<K> span() {
359      Cut<K> lowerBound;
360      Entry<Cut<K>, RangeMapEntry<K, V>> lowerEntry =
361          entriesByLowerBound.floorEntry(subRange.lowerBound);
362      if (lowerEntry != null
363          && lowerEntry.getValue().getUpperBound().compareTo(subRange.lowerBound) > 0) {
364        lowerBound = subRange.lowerBound;
365      } else {
366        lowerBound = entriesByLowerBound.ceilingKey(subRange.lowerBound);
367        if (lowerBound == null || lowerBound.compareTo(subRange.upperBound) >= 0) {
368          throw new NoSuchElementException();
369        }
370      }
371
372      Cut<K> upperBound;
373      Entry<Cut<K>, RangeMapEntry<K, V>> upperEntry =
374          entriesByLowerBound.lowerEntry(subRange.upperBound);
375      if (upperEntry == null) {
376        throw new NoSuchElementException();
377      } else if (upperEntry.getValue().getUpperBound().compareTo(subRange.upperBound) >= 0) {
378        upperBound = subRange.upperBound;
379      } else {
380        upperBound = upperEntry.getValue().getUpperBound();
381      }
382      return Range.create(lowerBound, upperBound);
383    }
384
385    @Override
386    public void put(Range<K> range, V value) {
387      checkArgument(
388          subRange.encloses(range), "Cannot put range %s into a subRangeMap(%s)", range, subRange);
389      TreeRangeMap.this.put(range, value);
390    }
391
392    @Override
393    public void putAll(RangeMap<K, V> rangeMap) {
394      if (rangeMap.asMapOfRanges().isEmpty()) {
395        return;
396      }
397      Range<K> span = rangeMap.span();
398      checkArgument(
399          subRange.encloses(span),
400          "Cannot putAll rangeMap with span %s into a subRangeMap(%s)",
401          span,
402          subRange);
403      TreeRangeMap.this.putAll(rangeMap);
404    }
405
406    @Override
407    public void clear() {
408      TreeRangeMap.this.remove(subRange);
409    }
410
411    @Override
412    public void remove(Range<K> range) {
413      if (range.isConnected(subRange)) {
414        TreeRangeMap.this.remove(range.intersection(subRange));
415      }
416    }
417
418    @Override
419    public RangeMap<K, V> subRangeMap(Range<K> range) {
420      if (!range.isConnected(subRange)) {
421        return emptySubRangeMap();
422      } else {
423        return TreeRangeMap.this.subRangeMap(range.intersection(subRange));
424      }
425    }
426
427    @Override
428    public Map<Range<K>, V> asMapOfRanges() {
429      return new SubRangeMapAsMap();
430    }
431
432    @Override
433    public Map<Range<K>, V> asDescendingMapOfRanges() {
434      return new SubRangeMapAsMap() {
435
436        @Override
437        Iterator<Entry<Range<K>, V>> entryIterator() {
438          if (subRange.isEmpty()) {
439            return Iterators.emptyIterator();
440          }
441          final Iterator<RangeMapEntry<K, V>> backingItr =
442              entriesByLowerBound
443                  .headMap(subRange.upperBound, false)
444                  .descendingMap()
445                  .values()
446                  .iterator();
447          return new AbstractIterator<Entry<Range<K>, V>>() {
448
449            @Override
450            protected Entry<Range<K>, V> computeNext() {
451              while (backingItr.hasNext()) {
452                RangeMapEntry<K, V> entry = backingItr.next();
453                if (entry.getUpperBound().compareTo(subRange.lowerBound) <= 0) {
454                  return endOfData();
455                }
456                return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
457              }
458              return endOfData();
459            }
460          };
461        }
462      };
463    }
464
465    @Override
466    public boolean equals(@Nullable Object o) {
467      if (o instanceof RangeMap) {
468        RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
469        return asMapOfRanges().equals(rangeMap.asMapOfRanges());
470      }
471      return false;
472    }
473
474    @Override
475    public int hashCode() {
476      return asMapOfRanges().hashCode();
477    }
478
479    @Override
480    public String toString() {
481      return asMapOfRanges().toString();
482    }
483
484    class SubRangeMapAsMap extends AbstractMap<Range<K>, V> {
485
486      @Override
487      public boolean containsKey(Object key) {
488        return get(key) != null;
489      }
490
491      @Override
492      public V get(Object key) {
493        try {
494          if (key instanceof Range) {
495            @SuppressWarnings("unchecked") // we catch ClassCastExceptions
496            Range<K> r = (Range<K>) key;
497            if (!subRange.encloses(r) || r.isEmpty()) {
498              return null;
499            }
500            RangeMapEntry<K, V> candidate = null;
501            if (r.lowerBound.compareTo(subRange.lowerBound) == 0) {
502              // r could be truncated on the left
503              Entry<Cut<K>, RangeMapEntry<K, V>> entry =
504                  entriesByLowerBound.floorEntry(r.lowerBound);
505              if (entry != null) {
506                candidate = entry.getValue();
507              }
508            } else {
509              candidate = entriesByLowerBound.get(r.lowerBound);
510            }
511
512            if (candidate != null
513                && candidate.getKey().isConnected(subRange)
514                && candidate.getKey().intersection(subRange).equals(r)) {
515              return candidate.getValue();
516            }
517          }
518        } catch (ClassCastException e) {
519          return null;
520        }
521        return null;
522      }
523
524      @Override
525      public V remove(Object key) {
526        V value = get(key);
527        if (value != null) {
528          @SuppressWarnings("unchecked") // it's definitely in the map, so safe
529          Range<K> range = (Range<K>) key;
530          TreeRangeMap.this.remove(range);
531          return value;
532        }
533        return null;
534      }
535
536      @Override
537      public void clear() {
538        SubRangeMap.this.clear();
539      }
540
541      private boolean removeEntryIf(Predicate<? super Entry<Range<K>, V>> predicate) {
542        List<Range<K>> toRemove = Lists.newArrayList();
543        for (Entry<Range<K>, V> entry : entrySet()) {
544          if (predicate.apply(entry)) {
545            toRemove.add(entry.getKey());
546          }
547        }
548        for (Range<K> range : toRemove) {
549          TreeRangeMap.this.remove(range);
550        }
551        return !toRemove.isEmpty();
552      }
553
554      @Override
555      public Set<Range<K>> keySet() {
556        return new Maps.KeySet<Range<K>, V>(SubRangeMapAsMap.this) {
557          @Override
558          public boolean remove(@Nullable Object o) {
559            return SubRangeMapAsMap.this.remove(o) != null;
560          }
561
562          @Override
563          public boolean retainAll(Collection<?> c) {
564            return removeEntryIf(compose(not(in(c)), Maps.<Range<K>>keyFunction()));
565          }
566        };
567      }
568
569      @Override
570      public Set<Entry<Range<K>, V>> entrySet() {
571        return new Maps.EntrySet<Range<K>, V>() {
572          @Override
573          Map<Range<K>, V> map() {
574            return SubRangeMapAsMap.this;
575          }
576
577          @Override
578          public Iterator<Entry<Range<K>, V>> iterator() {
579            return entryIterator();
580          }
581
582          @Override
583          public boolean retainAll(Collection<?> c) {
584            return removeEntryIf(not(in(c)));
585          }
586
587          @Override
588          public int size() {
589            return Iterators.size(iterator());
590          }
591
592          @Override
593          public boolean isEmpty() {
594            return !iterator().hasNext();
595          }
596        };
597      }
598
599      Iterator<Entry<Range<K>, V>> entryIterator() {
600        if (subRange.isEmpty()) {
601          return Iterators.emptyIterator();
602        }
603        Cut<K> cutToStart =
604            MoreObjects.firstNonNull(
605                entriesByLowerBound.floorKey(subRange.lowerBound), subRange.lowerBound);
606        final Iterator<RangeMapEntry<K, V>> backingItr =
607            entriesByLowerBound.tailMap(cutToStart, true).values().iterator();
608        return new AbstractIterator<Entry<Range<K>, V>>() {
609
610          @Override
611          protected Entry<Range<K>, V> computeNext() {
612            while (backingItr.hasNext()) {
613              RangeMapEntry<K, V> entry = backingItr.next();
614              if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) {
615                return endOfData();
616              } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) {
617                // this might not be true e.g. at the start of the iteration
618                return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
619              }
620            }
621            return endOfData();
622          }
623        };
624      }
625
626      @Override
627      public Collection<V> values() {
628        return new Maps.Values<Range<K>, V>(this) {
629          @Override
630          public boolean removeAll(Collection<?> c) {
631            return removeEntryIf(compose(in(c), Maps.<V>valueFunction()));
632          }
633
634          @Override
635          public boolean retainAll(Collection<?> c) {
636            return removeEntryIf(compose(not(in(c)), Maps.<V>valueFunction()));
637          }
638        };
639      }
640    }
641  }
642
643  @Override
644  public boolean equals(@Nullable Object o) {
645    if (o instanceof RangeMap) {
646      RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
647      return asMapOfRanges().equals(rangeMap.asMapOfRanges());
648    }
649    return false;
650  }
651
652  @Override
653  public int hashCode() {
654    return asMapOfRanges().hashCode();
655  }
656
657  @Override
658  public String toString() {
659    return entriesByLowerBound.values().toString();
660  }
661}