Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2012 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;
     18 
     19 import static com.google.common.base.Preconditions.checkArgument;
     20 import static com.google.common.base.Preconditions.checkNotNull;
     21 import static com.google.common.base.Predicates.compose;
     22 import static com.google.common.base.Predicates.in;
     23 import static com.google.common.base.Predicates.not;
     24 
     25 import com.google.common.annotations.Beta;
     26 import com.google.common.annotations.GwtIncompatible;
     27 import com.google.common.base.MoreObjects;
     28 import com.google.common.base.Predicate;
     29 
     30 import java.util.AbstractMap;
     31 import java.util.AbstractSet;
     32 import java.util.Collection;
     33 import java.util.Collections;
     34 import java.util.Iterator;
     35 import java.util.List;
     36 import java.util.Map;
     37 import java.util.Map.Entry;
     38 import java.util.NavigableMap;
     39 import java.util.NoSuchElementException;
     40 import java.util.Set;
     41 
     42 import javax.annotation.Nullable;
     43 
     44 /**
     45  * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting
     46  * all optional operations.
     47  *
     48  * <p>Like all {@code RangeMap} implementations, this supports neither null
     49  * keys nor null values.
     50  *
     51  * @author Louis Wasserman
     52  * @since 14.0
     53  */
     54 @Beta
     55 @GwtIncompatible("NavigableMap")
     56 public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K, V> {
     57 
     58   private final NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound;
     59 
     60   public static <K extends Comparable, V> TreeRangeMap<K, V> create() {
     61     return new TreeRangeMap<K, V>();
     62   }
     63 
     64   private TreeRangeMap() {
     65     this.entriesByLowerBound = Maps.newTreeMap();
     66   }
     67 
     68   private static final class RangeMapEntry<K extends Comparable, V>
     69       extends AbstractMapEntry<Range<K>, V> {
     70     private final Range<K> range;
     71     private final V value;
     72 
     73     RangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) {
     74       this(Range.create(lowerBound, upperBound), value);
     75     }
     76 
     77     RangeMapEntry(Range<K> range, V value) {
     78       this.range = range;
     79       this.value = value;
     80     }
     81 
     82     @Override
     83     public Range<K> getKey() {
     84       return range;
     85     }
     86 
     87     @Override
     88     public V getValue() {
     89       return value;
     90     }
     91 
     92     public boolean contains(K value) {
     93       return range.contains(value);
     94     }
     95 
     96     Cut<K> getLowerBound() {
     97       return range.lowerBound;
     98     }
     99 
    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,
    154         lastEntry.getValue().getKey().upperBound);
    155   }
    156 
    157   private void putRangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) {
    158     entriesByLowerBound.put(lowerBound, new RangeMapEntry<K, V>(lowerBound, upperBound, value));
    159   }
    160 
    161   @Override
    162   public void remove(Range<K> rangeToRemove) {
    163     if (rangeToRemove.isEmpty()) {
    164       return;
    165     }
    166 
    167     /*
    168      * The comments for this method will use [ ] to indicate the bounds of rangeToRemove and ( ) to
    169      * indicate the bounds of ranges in the range map.
    170      */
    171     Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryBelowToTruncate =
    172         entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound);
    173     if (mapEntryBelowToTruncate != null) {
    174       // we know ( [
    175       RangeMapEntry<K, V> rangeMapEntry = mapEntryBelowToTruncate.getValue();
    176       if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) {
    177         // we know ( [ )
    178         if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
    179           // we know ( [ ] ), so insert the range ] ) back into the map --
    180           // it's being split apart
    181           putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(),
    182               mapEntryBelowToTruncate.getValue().getValue());
    183         }
    184         // overwrite mapEntryToTruncateBelow with a truncated range
    185         putRangeMapEntry(rangeMapEntry.getLowerBound(), rangeToRemove.lowerBound,
    186             mapEntryBelowToTruncate.getValue().getValue());
    187       }
    188     }
    189 
    190     Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryAboveToTruncate =
    191         entriesByLowerBound.lowerEntry(rangeToRemove.upperBound);
    192     if (mapEntryAboveToTruncate != null) {
    193       // we know ( ]
    194       RangeMapEntry<K, V> rangeMapEntry = mapEntryAboveToTruncate.getValue();
    195       if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
    196         // we know ( ] ), and since we dealt with truncating below already,
    197         // we know [ ( ] )
    198         putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(),
    199             mapEntryAboveToTruncate.getValue().getValue());
    200         entriesByLowerBound.remove(rangeToRemove.lowerBound);
    201       }
    202     }
    203     entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear();
    204   }
    205 
    206   @Override
    207   public Map<Range<K>, V> asMapOfRanges() {
    208     return new AsMapOfRanges();
    209   }
    210 
    211   private final class AsMapOfRanges extends AbstractMap<Range<K>, V> {
    212 
    213     @Override
    214     public boolean containsKey(@Nullable Object key) {
    215       return get(key) != null;
    216     }
    217 
    218     @Override
    219     public V get(@Nullable Object key) {
    220       if (key instanceof Range) {
    221         Range<?> range = (Range<?>) key;
    222         RangeMapEntry<K, V> rangeMapEntry = entriesByLowerBound.get(range.lowerBound);
    223         if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) {
    224           return rangeMapEntry.getValue();
    225         }
    226       }
    227       return null;
    228     }
    229 
    230     @Override
    231     public Set<Entry<Range<K>, V>> entrySet() {
    232       return new AbstractSet<Entry<Range<K>, V>>() {
    233 
    234         @SuppressWarnings("unchecked") // it's safe to upcast iterators
    235         @Override
    236         public Iterator<Entry<Range<K>, V>> iterator() {
    237           return (Iterator) entriesByLowerBound.values().iterator();
    238         }
    239 
    240         @Override
    241         public int size() {
    242           return entriesByLowerBound.size();
    243         }
    244       };
    245     }
    246   }
    247 
    248   @Override
    249   public RangeMap<K, V> subRangeMap(Range<K> subRange) {
    250     if (subRange.equals(Range.all())) {
    251       return this;
    252     } else {
    253       return new SubRangeMap(subRange);
    254     }
    255   }
    256 
    257   @SuppressWarnings("unchecked")
    258   private RangeMap<K, V> emptySubRangeMap() {
    259     return EMPTY_SUB_RANGE_MAP;
    260   }
    261 
    262   private static final RangeMap EMPTY_SUB_RANGE_MAP =
    263       new RangeMap() {
    264         @Override
    265         @Nullable
    266         public Object get(Comparable key) {
    267           return null;
    268         }
    269 
    270         @Override
    271         @Nullable
    272         public Entry<Range, Object> getEntry(Comparable key) {
    273           return null;
    274         }
    275 
    276         @Override
    277         public Range span() {
    278           throw new NoSuchElementException();
    279         }
    280 
    281         @Override
    282         public void put(Range range, Object value) {
    283           checkNotNull(range);
    284           throw new IllegalArgumentException(
    285               "Cannot insert range " + range + " into an empty subRangeMap");
    286         }
    287 
    288         @Override
    289         public void putAll(RangeMap rangeMap) {
    290           if (!rangeMap.asMapOfRanges().isEmpty()) {
    291             throw new IllegalArgumentException(
    292                 "Cannot putAll(nonEmptyRangeMap) into an empty " + "subRangeMap");
    293           }
    294         }
    295 
    296         @Override
    297         public void clear() {}
    298 
    299         @Override
    300         public void remove(Range range) {
    301           checkNotNull(range);
    302         }
    303 
    304         @Override
    305         public Map<Range, Object> asMapOfRanges() {
    306           return Collections.emptyMap();
    307         }
    308 
    309         @Override
    310         public RangeMap subRangeMap(Range range) {
    311           checkNotNull(range);
    312           return this;
    313         }
    314       };
    315 
    316   private class SubRangeMap implements RangeMap<K, V> {
    317 
    318     private final Range<K> subRange;
    319 
    320     SubRangeMap(Range<K> subRange) {
    321       this.subRange = subRange;
    322     }
    323 
    324     @Override
    325     @Nullable
    326     public V get(K key) {
    327       return subRange.contains(key)
    328           ? TreeRangeMap.this.get(key)
    329           : null;
    330     }
    331 
    332     @Override
    333     @Nullable
    334     public Entry<Range<K>, V> getEntry(K key) {
    335       if (subRange.contains(key)) {
    336         Entry<Range<K>, V> entry = TreeRangeMap.this.getEntry(key);
    337         if (entry != null) {
    338           return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue());
    339         }
    340       }
    341       return null;
    342     }
    343 
    344     @Override
    345     public Range<K> span() {
    346       Cut<K> lowerBound;
    347       Entry<Cut<K>, RangeMapEntry<K, V>> lowerEntry =
    348           entriesByLowerBound.floorEntry(subRange.lowerBound);
    349       if (lowerEntry != null &&
    350           lowerEntry.getValue().getUpperBound().compareTo(subRange.lowerBound) > 0) {
    351         lowerBound = subRange.lowerBound;
    352       } else {
    353         lowerBound = entriesByLowerBound.ceilingKey(subRange.lowerBound);
    354         if (lowerBound == null || lowerBound.compareTo(subRange.upperBound) >= 0) {
    355           throw new NoSuchElementException();
    356         }
    357       }
    358 
    359       Cut<K> upperBound;
    360       Entry<Cut<K>, RangeMapEntry<K, V>> upperEntry =
    361           entriesByLowerBound.lowerEntry(subRange.upperBound);
    362       if (upperEntry == null) {
    363         throw new NoSuchElementException();
    364       } else if (upperEntry.getValue().getUpperBound().compareTo(subRange.upperBound) >= 0) {
    365         upperBound = subRange.upperBound;
    366       } else {
    367         upperBound = upperEntry.getValue().getUpperBound();
    368       }
    369       return Range.create(lowerBound, upperBound);
    370     }
    371 
    372     @Override
    373     public void put(Range<K> range, V value) {
    374       checkArgument(subRange.encloses(range),
    375           "Cannot put range %s into a subRangeMap(%s)", range, subRange);
    376       TreeRangeMap.this.put(range, value);
    377     }
    378 
    379     @Override
    380     public void putAll(RangeMap<K, V> rangeMap) {
    381       if (rangeMap.asMapOfRanges().isEmpty()) {
    382         return;
    383       }
    384       Range<K> span = rangeMap.span();
    385       checkArgument(subRange.encloses(span),
    386           "Cannot putAll rangeMap with span %s into a subRangeMap(%s)", span, subRange);
    387       TreeRangeMap.this.putAll(rangeMap);
    388     }
    389 
    390     @Override
    391     public void clear() {
    392       TreeRangeMap.this.remove(subRange);
    393     }
    394 
    395     @Override
    396     public void remove(Range<K> range) {
    397       if (range.isConnected(subRange)) {
    398         TreeRangeMap.this.remove(range.intersection(subRange));
    399       }
    400     }
    401 
    402     @Override
    403     public RangeMap<K, V> subRangeMap(Range<K> range) {
    404       if (!range.isConnected(subRange)) {
    405         return emptySubRangeMap();
    406       } else {
    407         return TreeRangeMap.this.subRangeMap(range.intersection(subRange));
    408       }
    409     }
    410 
    411     @Override
    412     public Map<Range<K>, V> asMapOfRanges() {
    413       return new SubRangeMapAsMap();
    414     }
    415 
    416     @Override
    417     public boolean equals(@Nullable Object o) {
    418       if (o instanceof RangeMap) {
    419         RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
    420         return asMapOfRanges().equals(rangeMap.asMapOfRanges());
    421       }
    422       return false;
    423     }
    424 
    425     @Override
    426     public int hashCode() {
    427       return asMapOfRanges().hashCode();
    428     }
    429 
    430     @Override
    431     public String toString() {
    432       return asMapOfRanges().toString();
    433     }
    434 
    435     class SubRangeMapAsMap extends AbstractMap<Range<K>, V> {
    436 
    437       @Override
    438       public boolean containsKey(Object key) {
    439         return get(key) != null;
    440       }
    441 
    442       @Override
    443       public V get(Object key) {
    444         try {
    445           if (key instanceof Range) {
    446             @SuppressWarnings("unchecked") // we catch ClassCastExceptions
    447             Range<K> r = (Range<K>) key;
    448             if (!subRange.encloses(r) || r.isEmpty()) {
    449               return null;
    450             }
    451             RangeMapEntry<K, V> candidate = null;
    452             if (r.lowerBound.compareTo(subRange.lowerBound) == 0) {
    453               // r could be truncated on the left
    454               Entry<Cut<K>, RangeMapEntry<K, V>> entry =
    455                   entriesByLowerBound.floorEntry(r.lowerBound);
    456               if (entry != null) {
    457                 candidate = entry.getValue();
    458               }
    459             } else {
    460               candidate = entriesByLowerBound.get(r.lowerBound);
    461             }
    462 
    463             if (candidate != null && candidate.getKey().isConnected(subRange)
    464                 && candidate.getKey().intersection(subRange).equals(r)) {
    465               return candidate.getValue();
    466             }
    467           }
    468         } catch (ClassCastException e) {
    469           return null;
    470         }
    471         return null;
    472       }
    473 
    474       @Override
    475       public V remove(Object key) {
    476         V value = get(key);
    477         if (value != null) {
    478           @SuppressWarnings("unchecked") // it's definitely in the map, so safe
    479           Range<K> range = (Range<K>) key;
    480           TreeRangeMap.this.remove(range);
    481           return value;
    482         }
    483         return null;
    484       }
    485 
    486       @Override
    487       public void clear() {
    488         SubRangeMap.this.clear();
    489       }
    490 
    491       private boolean removeEntryIf(Predicate<? super Entry<Range<K>, V>> predicate) {
    492         List<Range<K>> toRemove = Lists.newArrayList();
    493         for (Entry<Range<K>, V> entry : entrySet()) {
    494           if (predicate.apply(entry)) {
    495             toRemove.add(entry.getKey());
    496           }
    497         }
    498         for (Range<K> range : toRemove) {
    499           TreeRangeMap.this.remove(range);
    500         }
    501         return !toRemove.isEmpty();
    502       }
    503 
    504       @Override
    505       public Set<Range<K>> keySet() {
    506         return new Maps.KeySet<Range<K>, V>(SubRangeMapAsMap.this) {
    507           @Override
    508           public boolean remove(@Nullable Object o) {
    509             return SubRangeMapAsMap.this.remove(o) != null;
    510           }
    511 
    512           @Override
    513           public boolean retainAll(Collection<?> c) {
    514             return removeEntryIf(compose(not(in(c)), Maps.<Range<K>>keyFunction()));
    515           }
    516         };
    517       }
    518 
    519       @Override
    520       public Set<Entry<Range<K>, V>> entrySet() {
    521         return new Maps.EntrySet<Range<K>, V>() {
    522           @Override
    523           Map<Range<K>, V> map() {
    524             return SubRangeMapAsMap.this;
    525           }
    526 
    527           @Override
    528           public Iterator<Entry<Range<K>, V>> iterator() {
    529             if (subRange.isEmpty()) {
    530               return Iterators.emptyIterator();
    531             }
    532             Cut<K> cutToStart = MoreObjects.firstNonNull(
    533                 entriesByLowerBound.floorKey(subRange.lowerBound), subRange.lowerBound);
    534             final Iterator<RangeMapEntry<K, V>> backingItr =
    535                 entriesByLowerBound.tailMap(cutToStart, true).values().iterator();
    536             return new AbstractIterator<Entry<Range<K>, V>>() {
    537 
    538               @Override
    539               protected Entry<Range<K>, V> computeNext() {
    540                 while (backingItr.hasNext()) {
    541                   RangeMapEntry<K, V> entry = backingItr.next();
    542                   if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) {
    543                     break;
    544                   } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) {
    545                     // this might not be true e.g. at the start of the iteration
    546                     return Maps.immutableEntry(
    547                         entry.getKey().intersection(subRange), entry.getValue());
    548                   }
    549                 }
    550                 return endOfData();
    551               }
    552             };
    553           }
    554 
    555           @Override
    556           public boolean retainAll(Collection<?> c) {
    557             return removeEntryIf(not(in(c)));
    558           }
    559 
    560           @Override
    561           public int size() {
    562             return Iterators.size(iterator());
    563           }
    564 
    565           @Override
    566           public boolean isEmpty() {
    567             return !iterator().hasNext();
    568           }
    569         };
    570       }
    571 
    572       @Override
    573       public Collection<V> values() {
    574         return new Maps.Values<Range<K>, V>(this) {
    575           @Override
    576           public boolean removeAll(Collection<?> c) {
    577             return removeEntryIf(compose(in(c), Maps.<V>valueFunction()));
    578           }
    579 
    580           @Override
    581           public boolean retainAll(Collection<?> c) {
    582             return removeEntryIf(compose(not(in(c)), Maps.<V>valueFunction()));
    583           }
    584         };
    585       }
    586     }
    587   }
    588 
    589   @Override
    590   public boolean equals(@Nullable Object o) {
    591     if (o instanceof RangeMap) {
    592       RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
    593       return asMapOfRanges().equals(rangeMap.asMapOfRanges());
    594     }
    595     return false;
    596   }
    597 
    598   @Override
    599   public int hashCode() {
    600     return asMapOfRanges().hashCode();
    601   }
    602 
    603   @Override
    604   public String toString() {
    605     return entriesByLowerBound.values().toString();
    606   }
    607 }
    608