001/*
002 * Copyright (C) 2012 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.collect;
016
017import static com.google.common.base.Preconditions.checkArgument;
018import static com.google.common.base.Preconditions.checkElementIndex;
019import static com.google.common.base.Preconditions.checkNotNull;
020
021import com.google.common.annotations.Beta;
022import com.google.common.annotations.GwtIncompatible;
023import com.google.common.collect.SortedLists.KeyAbsentBehavior;
024import com.google.common.collect.SortedLists.KeyPresentBehavior;
025import com.google.errorprone.annotations.CanIgnoreReturnValue;
026import java.io.Serializable;
027import java.util.Collections;
028import java.util.List;
029import java.util.Map;
030import java.util.Map.Entry;
031import java.util.NoSuchElementException;
032import org.checkerframework.checker.nullness.compatqual.NullableDecl;
033
034/**
035 * A {@link RangeMap} whose contents will never change, with many other important properties
036 * detailed at {@link ImmutableCollection}.
037 *
038 * @author Louis Wasserman
039 * @since 14.0
040 */
041@Beta
042@GwtIncompatible // NavigableMap
043public class ImmutableRangeMap<K extends Comparable<?>, V> implements RangeMap<K, V>, Serializable {
044
045  private static final ImmutableRangeMap<Comparable<?>, Object> EMPTY =
046      new ImmutableRangeMap<>(ImmutableList.<Range<Comparable<?>>>of(), ImmutableList.of());
047
048  /** Returns an empty immutable range map. */
049  @SuppressWarnings("unchecked")
050  public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of() {
051    return (ImmutableRangeMap<K, V>) EMPTY;
052  }
053
054  /** Returns an immutable range map mapping a single range to a single value. */
055  public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of(Range<K> range, V value) {
056    return new ImmutableRangeMap<>(ImmutableList.of(range), ImmutableList.of(value));
057  }
058
059  @SuppressWarnings("unchecked")
060  public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> copyOf(
061      RangeMap<K, ? extends V> rangeMap) {
062    if (rangeMap instanceof ImmutableRangeMap) {
063      return (ImmutableRangeMap<K, V>) rangeMap;
064    }
065    Map<Range<K>, ? extends V> map = rangeMap.asMapOfRanges();
066    ImmutableList.Builder<Range<K>> rangesBuilder = new ImmutableList.Builder<>(map.size());
067    ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<V>(map.size());
068    for (Entry<Range<K>, ? extends V> entry : map.entrySet()) {
069      rangesBuilder.add(entry.getKey());
070      valuesBuilder.add(entry.getValue());
071    }
072    return new ImmutableRangeMap<>(rangesBuilder.build(), valuesBuilder.build());
073  }
074
075  /** Returns a new builder for an immutable range map. */
076  public static <K extends Comparable<?>, V> Builder<K, V> builder() {
077    return new Builder<>();
078  }
079
080  /**
081   * A builder for immutable range maps. Overlapping ranges are prohibited.
082   *
083   * @since 14.0
084   */
085  public static final class Builder<K extends Comparable<?>, V> {
086    private final List<Entry<Range<K>, V>> entries;
087
088    public Builder() {
089      this.entries = Lists.newArrayList();
090    }
091
092    /**
093     * Associates the specified range with the specified value.
094     *
095     * @throws IllegalArgumentException if {@code range} is empty
096     */
097    @CanIgnoreReturnValue
098    public Builder<K, V> put(Range<K> range, V value) {
099      checkNotNull(range);
100      checkNotNull(value);
101      checkArgument(!range.isEmpty(), "Range must not be empty, but was %s", range);
102      entries.add(Maps.immutableEntry(range, value));
103      return this;
104    }
105
106    /** Copies all associations from the specified range map into this builder. */
107    @CanIgnoreReturnValue
108    public Builder<K, V> putAll(RangeMap<K, ? extends V> rangeMap) {
109      for (Entry<Range<K>, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) {
110        put(entry.getKey(), entry.getValue());
111      }
112      return this;
113    }
114
115    /**
116     * Returns an {@code ImmutableRangeMap} containing the associations previously added to this
117     * builder.
118     *
119     * @throws IllegalArgumentException if any two ranges inserted into this builder overlap
120     */
121    public ImmutableRangeMap<K, V> build() {
122      Collections.sort(entries, Range.<K>rangeLexOrdering().onKeys());
123      ImmutableList.Builder<Range<K>> rangesBuilder = new ImmutableList.Builder<>(entries.size());
124      ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<V>(entries.size());
125      for (int i = 0; i < entries.size(); i++) {
126        Range<K> range = entries.get(i).getKey();
127        if (i > 0) {
128          Range<K> prevRange = entries.get(i - 1).getKey();
129          if (range.isConnected(prevRange) && !range.intersection(prevRange).isEmpty()) {
130            throw new IllegalArgumentException(
131                "Overlapping ranges: range " + prevRange + " overlaps with entry " + range);
132          }
133        }
134        rangesBuilder.add(range);
135        valuesBuilder.add(entries.get(i).getValue());
136      }
137      return new ImmutableRangeMap<>(rangesBuilder.build(), valuesBuilder.build());
138    }
139  }
140
141  private final transient ImmutableList<Range<K>> ranges;
142  private final transient ImmutableList<V> values;
143
144  ImmutableRangeMap(ImmutableList<Range<K>> ranges, ImmutableList<V> values) {
145    this.ranges = ranges;
146    this.values = values;
147  }
148
149  @Override
150  @NullableDecl
151  public V get(K key) {
152    int index =
153        SortedLists.binarySearch(
154            ranges,
155            Range.<K>lowerBoundFn(),
156            Cut.belowValue(key),
157            KeyPresentBehavior.ANY_PRESENT,
158            KeyAbsentBehavior.NEXT_LOWER);
159    if (index == -1) {
160      return null;
161    } else {
162      Range<K> range = ranges.get(index);
163      return range.contains(key) ? values.get(index) : null;
164    }
165  }
166
167  @Override
168  @NullableDecl
169  public Entry<Range<K>, V> getEntry(K key) {
170    int index =
171        SortedLists.binarySearch(
172            ranges,
173            Range.<K>lowerBoundFn(),
174            Cut.belowValue(key),
175            KeyPresentBehavior.ANY_PRESENT,
176            KeyAbsentBehavior.NEXT_LOWER);
177    if (index == -1) {
178      return null;
179    } else {
180      Range<K> range = ranges.get(index);
181      return range.contains(key) ? Maps.immutableEntry(range, values.get(index)) : null;
182    }
183  }
184
185  @Override
186  public Range<K> span() {
187    if (ranges.isEmpty()) {
188      throw new NoSuchElementException();
189    }
190    Range<K> firstRange = ranges.get(0);
191    Range<K> lastRange = ranges.get(ranges.size() - 1);
192    return Range.create(firstRange.lowerBound, lastRange.upperBound);
193  }
194
195  /**
196   * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
197   *
198   * @throws UnsupportedOperationException always
199   * @deprecated Unsupported operation.
200   */
201  @Deprecated
202  @Override
203  public void put(Range<K> range, V value) {
204    throw new UnsupportedOperationException();
205  }
206
207  /**
208   * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
209   *
210   * @throws UnsupportedOperationException always
211   * @deprecated Unsupported operation.
212   */
213  @Deprecated
214  @Override
215  public void putCoalescing(Range<K> range, V value) {
216    throw new UnsupportedOperationException();
217  }
218
219  /**
220   * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
221   *
222   * @throws UnsupportedOperationException always
223   * @deprecated Unsupported operation.
224   */
225  @Deprecated
226  @Override
227  public void putAll(RangeMap<K, V> rangeMap) {
228    throw new UnsupportedOperationException();
229  }
230
231  /**
232   * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
233   *
234   * @throws UnsupportedOperationException always
235   * @deprecated Unsupported operation.
236   */
237  @Deprecated
238  @Override
239  public void clear() {
240    throw new UnsupportedOperationException();
241  }
242
243  /**
244   * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
245   *
246   * @throws UnsupportedOperationException always
247   * @deprecated Unsupported operation.
248   */
249  @Deprecated
250  @Override
251  public void remove(Range<K> range) {
252    throw new UnsupportedOperationException();
253  }
254
255  @Override
256  public ImmutableMap<Range<K>, V> asMapOfRanges() {
257    if (ranges.isEmpty()) {
258      return ImmutableMap.of();
259    }
260    RegularImmutableSortedSet<Range<K>> rangeSet =
261        new RegularImmutableSortedSet<>(ranges, Range.<K>rangeLexOrdering());
262    return new ImmutableSortedMap<>(rangeSet, values);
263  }
264
265  @Override
266  public ImmutableMap<Range<K>, V> asDescendingMapOfRanges() {
267    if (ranges.isEmpty()) {
268      return ImmutableMap.of();
269    }
270    RegularImmutableSortedSet<Range<K>> rangeSet =
271        new RegularImmutableSortedSet<>(ranges.reverse(), Range.<K>rangeLexOrdering().reverse());
272    return new ImmutableSortedMap<>(rangeSet, values.reverse());
273  }
274
275  @Override
276  public ImmutableRangeMap<K, V> subRangeMap(final Range<K> range) {
277    if (checkNotNull(range).isEmpty()) {
278      return ImmutableRangeMap.of();
279    } else if (ranges.isEmpty() || range.encloses(span())) {
280      return this;
281    }
282    int lowerIndex =
283        SortedLists.binarySearch(
284            ranges,
285            Range.<K>upperBoundFn(),
286            range.lowerBound,
287            KeyPresentBehavior.FIRST_AFTER,
288            KeyAbsentBehavior.NEXT_HIGHER);
289    int upperIndex =
290        SortedLists.binarySearch(
291            ranges,
292            Range.<K>lowerBoundFn(),
293            range.upperBound,
294            KeyPresentBehavior.ANY_PRESENT,
295            KeyAbsentBehavior.NEXT_HIGHER);
296    if (lowerIndex >= upperIndex) {
297      return ImmutableRangeMap.of();
298    }
299    final int off = lowerIndex;
300    final int len = upperIndex - lowerIndex;
301    ImmutableList<Range<K>> subRanges =
302        new ImmutableList<Range<K>>() {
303          @Override
304          public int size() {
305            return len;
306          }
307
308          @Override
309          public Range<K> get(int index) {
310            checkElementIndex(index, len);
311            if (index == 0 || index == len - 1) {
312              return ranges.get(index + off).intersection(range);
313            } else {
314              return ranges.get(index + off);
315            }
316          }
317
318          @Override
319          boolean isPartialView() {
320            return true;
321          }
322        };
323    final ImmutableRangeMap<K, V> outer = this;
324    return new ImmutableRangeMap<K, V>(subRanges, values.subList(lowerIndex, upperIndex)) {
325      @Override
326      public ImmutableRangeMap<K, V> subRangeMap(Range<K> subRange) {
327        if (range.isConnected(subRange)) {
328          return outer.subRangeMap(subRange.intersection(range));
329        } else {
330          return ImmutableRangeMap.of();
331        }
332      }
333    };
334  }
335
336  @Override
337  public int hashCode() {
338    return asMapOfRanges().hashCode();
339  }
340
341  @Override
342  public boolean equals(@NullableDecl Object o) {
343    if (o instanceof RangeMap) {
344      RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
345      return asMapOfRanges().equals(rangeMap.asMapOfRanges());
346    }
347    return false;
348  }
349
350  @Override
351  public String toString() {
352    return asMapOfRanges().toString();
353  }
354
355  /**
356   * This class is used to serialize ImmutableRangeMap instances. Serializes the {@link
357   * #asMapOfRanges()} form.
358   */
359  private static class SerializedForm<K extends Comparable<?>, V> implements Serializable {
360
361    private final ImmutableMap<Range<K>, V> mapOfRanges;
362
363    SerializedForm(ImmutableMap<Range<K>, V> mapOfRanges) {
364      this.mapOfRanges = mapOfRanges;
365    }
366
367    Object readResolve() {
368      if (mapOfRanges.isEmpty()) {
369        return of();
370      } else {
371        return createRangeMap();
372      }
373    }
374
375    Object createRangeMap() {
376      Builder<K, V> builder = new Builder<>();
377      for (Entry<Range<K>, V> entry : mapOfRanges.entrySet()) {
378        builder.put(entry.getKey(), entry.getValue());
379      }
380      return builder.build();
381    }
382
383    private static final long serialVersionUID = 0;
384  }
385
386  Object writeReplace() {
387    return new SerializedForm<>(asMapOfRanges());
388  }
389
390  private static final long serialVersionUID = 0;
391}