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;
020import static com.google.common.collect.Maps.immutableEntry;
021import static java.util.Collections.sort;
022
023import com.google.common.annotations.GwtIncompatible;
024import com.google.common.annotations.J2ktIncompatible;
025import com.google.common.collect.SortedLists.KeyAbsentBehavior;
026import com.google.common.collect.SortedLists.KeyPresentBehavior;
027import com.google.errorprone.annotations.CanIgnoreReturnValue;
028import com.google.errorprone.annotations.DoNotCall;
029import com.google.errorprone.annotations.DoNotMock;
030import java.io.InvalidObjectException;
031import java.io.ObjectInputStream;
032import java.io.Serializable;
033import java.util.List;
034import java.util.Map;
035import java.util.Map.Entry;
036import java.util.NoSuchElementException;
037import java.util.function.Function;
038import java.util.stream.Collector;
039import javax.annotation.CheckForNull;
040import org.checkerframework.checker.nullness.qual.Nullable;
041
042/**
043 * A {@link RangeMap} whose contents will never change, with many other important properties
044 * detailed at {@link ImmutableCollection}.
045 *
046 * @author Louis Wasserman
047 * @since 14.0
048 */
049@GwtIncompatible // NavigableMap
050@ElementTypesAreNonnullByDefault
051public class ImmutableRangeMap<K extends Comparable<?>, V> implements RangeMap<K, V>, Serializable {
052
053  private static final ImmutableRangeMap<Comparable<?>, Object> EMPTY =
054      new ImmutableRangeMap<>(ImmutableList.<Range<Comparable<?>>>of(), ImmutableList.of());
055
056  /**
057   * Returns a {@code Collector} that accumulates the input elements into a new {@code
058   * ImmutableRangeMap}. As in {@link Builder}, overlapping ranges are not permitted.
059   *
060   * @since 33.2.0 (available since 23.1 in guava-jre)
061   */
062  @SuppressWarnings("Java7ApiChecker")
063  @IgnoreJRERequirement // Users will use this only if they're already using streams.
064  public static <T extends @Nullable Object, K extends Comparable<? super K>, V>
065      Collector<T, ?, ImmutableRangeMap<K, V>> toImmutableRangeMap(
066          Function<? super T, Range<K>> keyFunction,
067          Function<? super T, ? extends V> valueFunction) {
068    return CollectCollectors.toImmutableRangeMap(keyFunction, valueFunction);
069  }
070
071  /**
072   * Returns an empty immutable range map.
073   *
074   * <p><b>Performance note:</b> the instance returned is a singleton.
075   */
076  @SuppressWarnings("unchecked")
077  public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of() {
078    return (ImmutableRangeMap<K, V>) EMPTY;
079  }
080
081  /** Returns an immutable range map mapping a single range to a single value. */
082  public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of(Range<K> range, V value) {
083    return new ImmutableRangeMap<>(ImmutableList.of(range), ImmutableList.of(value));
084  }
085
086  @SuppressWarnings("unchecked")
087  public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> copyOf(
088      RangeMap<K, ? extends V> rangeMap) {
089    if (rangeMap instanceof ImmutableRangeMap) {
090      return (ImmutableRangeMap<K, V>) rangeMap;
091    }
092    Map<Range<K>, ? extends V> map = rangeMap.asMapOfRanges();
093    ImmutableList.Builder<Range<K>> rangesBuilder = new ImmutableList.Builder<>(map.size());
094    ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<>(map.size());
095    for (Entry<Range<K>, ? extends V> entry : map.entrySet()) {
096      rangesBuilder.add(entry.getKey());
097      valuesBuilder.add(entry.getValue());
098    }
099    return new ImmutableRangeMap<>(rangesBuilder.build(), valuesBuilder.build());
100  }
101
102  /** Returns a new builder for an immutable range map. */
103  public static <K extends Comparable<?>, V> Builder<K, V> builder() {
104    return new Builder<>();
105  }
106
107  /**
108   * A builder for immutable range maps. Overlapping ranges are prohibited.
109   *
110   * @since 14.0
111   */
112  @DoNotMock
113  public static final class Builder<K extends Comparable<?>, V> {
114    private final List<Entry<Range<K>, V>> entries;
115
116    public Builder() {
117      this.entries = Lists.newArrayList();
118    }
119
120    /**
121     * Associates the specified range with the specified value.
122     *
123     * @throws IllegalArgumentException if {@code range} is empty
124     */
125    @CanIgnoreReturnValue
126    public Builder<K, V> put(Range<K> range, V value) {
127      checkNotNull(range);
128      checkNotNull(value);
129      checkArgument(!range.isEmpty(), "Range must not be empty, but was %s", range);
130      entries.add(immutableEntry(range, value));
131      return this;
132    }
133
134    /** Copies all associations from the specified range map into this builder. */
135    @CanIgnoreReturnValue
136    public Builder<K, V> putAll(RangeMap<K, ? extends V> rangeMap) {
137      for (Entry<Range<K>, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) {
138        put(entry.getKey(), entry.getValue());
139      }
140      return this;
141    }
142
143    @CanIgnoreReturnValue
144    Builder<K, V> combine(Builder<K, V> builder) {
145      entries.addAll(builder.entries);
146      return this;
147    }
148
149    /**
150     * Returns an {@code ImmutableRangeMap} containing the associations previously added to this
151     * builder.
152     *
153     * @throws IllegalArgumentException if any two ranges inserted into this builder overlap
154     */
155    public ImmutableRangeMap<K, V> build() {
156      sort(entries, Range.<K>rangeLexOrdering().onKeys());
157      ImmutableList.Builder<Range<K>> rangesBuilder = new ImmutableList.Builder<>(entries.size());
158      ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<>(entries.size());
159      for (int i = 0; i < entries.size(); i++) {
160        Range<K> range = entries.get(i).getKey();
161        if (i > 0) {
162          Range<K> prevRange = entries.get(i - 1).getKey();
163          if (range.isConnected(prevRange) && !range.intersection(prevRange).isEmpty()) {
164            throw new IllegalArgumentException(
165                "Overlapping ranges: range " + prevRange + " overlaps with entry " + range);
166          }
167        }
168        rangesBuilder.add(range);
169        valuesBuilder.add(entries.get(i).getValue());
170      }
171      return new ImmutableRangeMap<>(rangesBuilder.build(), valuesBuilder.build());
172    }
173  }
174
175  private final transient ImmutableList<Range<K>> ranges;
176  private final transient ImmutableList<V> values;
177
178  ImmutableRangeMap(ImmutableList<Range<K>> ranges, ImmutableList<V> values) {
179    this.ranges = ranges;
180    this.values = values;
181  }
182
183  @Override
184  @CheckForNull
185  public V get(K key) {
186    int index =
187        SortedLists.binarySearch(
188            ranges,
189            Range::lowerBound,
190            Cut.belowValue(key),
191            KeyPresentBehavior.ANY_PRESENT,
192            KeyAbsentBehavior.NEXT_LOWER);
193    if (index == -1) {
194      return null;
195    } else {
196      Range<K> range = ranges.get(index);
197      return range.contains(key) ? values.get(index) : null;
198    }
199  }
200
201  @Override
202  @CheckForNull
203  public Entry<Range<K>, V> getEntry(K key) {
204    int index =
205        SortedLists.binarySearch(
206            ranges,
207            Range::lowerBound,
208            Cut.belowValue(key),
209            KeyPresentBehavior.ANY_PRESENT,
210            KeyAbsentBehavior.NEXT_LOWER);
211    if (index == -1) {
212      return null;
213    } else {
214      Range<K> range = ranges.get(index);
215      return range.contains(key) ? immutableEntry(range, values.get(index)) : null;
216    }
217  }
218
219  @Override
220  public Range<K> span() {
221    if (ranges.isEmpty()) {
222      throw new NoSuchElementException();
223    }
224    Range<K> firstRange = ranges.get(0);
225    Range<K> lastRange = ranges.get(ranges.size() - 1);
226    return Range.create(firstRange.lowerBound, lastRange.upperBound);
227  }
228
229  /**
230   * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
231   *
232   * @throws UnsupportedOperationException always
233   * @deprecated Unsupported operation.
234   */
235  @Deprecated
236  @Override
237  @DoNotCall("Always throws UnsupportedOperationException")
238  public final void put(Range<K> range, V value) {
239    throw new UnsupportedOperationException();
240  }
241
242  /**
243   * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
244   *
245   * @throws UnsupportedOperationException always
246   * @deprecated Unsupported operation.
247   */
248  @Deprecated
249  @Override
250  @DoNotCall("Always throws UnsupportedOperationException")
251  public final void putCoalescing(Range<K> range, V value) {
252    throw new UnsupportedOperationException();
253  }
254
255  /**
256   * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
257   *
258   * @throws UnsupportedOperationException always
259   * @deprecated Unsupported operation.
260   */
261  @Deprecated
262  @Override
263  @DoNotCall("Always throws UnsupportedOperationException")
264  public final void putAll(RangeMap<K, ? extends V> rangeMap) {
265    throw new UnsupportedOperationException();
266  }
267
268  /**
269   * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
270   *
271   * @throws UnsupportedOperationException always
272   * @deprecated Unsupported operation.
273   */
274  @Deprecated
275  @Override
276  @DoNotCall("Always throws UnsupportedOperationException")
277  public final void clear() {
278    throw new UnsupportedOperationException();
279  }
280
281  /**
282   * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
283   *
284   * @throws UnsupportedOperationException always
285   * @deprecated Unsupported operation.
286   */
287  @Deprecated
288  @Override
289  @DoNotCall("Always throws UnsupportedOperationException")
290  public final void remove(Range<K> range) {
291    throw new UnsupportedOperationException();
292  }
293
294  @Override
295  public ImmutableMap<Range<K>, V> asMapOfRanges() {
296    if (ranges.isEmpty()) {
297      return ImmutableMap.of();
298    }
299    RegularImmutableSortedSet<Range<K>> rangeSet =
300        new RegularImmutableSortedSet<>(ranges, Range.<K>rangeLexOrdering());
301    return new ImmutableSortedMap<>(rangeSet, values);
302  }
303
304  @Override
305  public ImmutableMap<Range<K>, V> asDescendingMapOfRanges() {
306    if (ranges.isEmpty()) {
307      return ImmutableMap.of();
308    }
309    RegularImmutableSortedSet<Range<K>> rangeSet =
310        new RegularImmutableSortedSet<>(ranges.reverse(), Range.<K>rangeLexOrdering().reverse());
311    return new ImmutableSortedMap<>(rangeSet, values.reverse());
312  }
313
314  @Override
315  public ImmutableRangeMap<K, V> subRangeMap(final Range<K> range) {
316    if (checkNotNull(range).isEmpty()) {
317      return ImmutableRangeMap.of();
318    } else if (ranges.isEmpty() || range.encloses(span())) {
319      return this;
320    }
321    int lowerIndex =
322        SortedLists.binarySearch(
323            ranges,
324            Range::upperBound,
325            range.lowerBound,
326            KeyPresentBehavior.FIRST_AFTER,
327            KeyAbsentBehavior.NEXT_HIGHER);
328    int upperIndex =
329        SortedLists.binarySearch(
330            ranges,
331            Range::lowerBound,
332            range.upperBound,
333            KeyPresentBehavior.ANY_PRESENT,
334            KeyAbsentBehavior.NEXT_HIGHER);
335    if (lowerIndex >= upperIndex) {
336      return ImmutableRangeMap.of();
337    }
338    final int off = lowerIndex;
339    final int len = upperIndex - lowerIndex;
340    ImmutableList<Range<K>> subRanges =
341        new ImmutableList<Range<K>>() {
342          @Override
343          public int size() {
344            return len;
345          }
346
347          @Override
348          public Range<K> get(int index) {
349            checkElementIndex(index, len);
350            if (index == 0 || index == len - 1) {
351              return ranges.get(index + off).intersection(range);
352            } else {
353              return ranges.get(index + off);
354            }
355          }
356
357          @Override
358          boolean isPartialView() {
359            return true;
360          }
361
362          // redeclare to help optimizers with b/310253115
363          @SuppressWarnings("RedundantOverride")
364          @Override
365          @J2ktIncompatible // serialization
366          Object writeReplace() {
367            return super.writeReplace();
368          }
369        };
370    final ImmutableRangeMap<K, V> outer = this;
371    return new ImmutableRangeMap<K, V>(subRanges, values.subList(lowerIndex, upperIndex)) {
372      @Override
373      public ImmutableRangeMap<K, V> subRangeMap(Range<K> subRange) {
374        if (range.isConnected(subRange)) {
375          return outer.subRangeMap(subRange.intersection(range));
376        } else {
377          return ImmutableRangeMap.of();
378        }
379      }
380
381      // redeclare to help optimizers with b/310253115
382      @SuppressWarnings("RedundantOverride")
383      @Override
384      @J2ktIncompatible // serialization
385      Object writeReplace() {
386        return super.writeReplace();
387      }
388    };
389  }
390
391  @Override
392  public int hashCode() {
393    return asMapOfRanges().hashCode();
394  }
395
396  @Override
397  public boolean equals(@CheckForNull Object o) {
398    if (o instanceof RangeMap) {
399      RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
400      return asMapOfRanges().equals(rangeMap.asMapOfRanges());
401    }
402    return false;
403  }
404
405  @Override
406  public String toString() {
407    return asMapOfRanges().toString();
408  }
409
410  /**
411   * This class is used to serialize ImmutableRangeMap instances. Serializes the {@link
412   * #asMapOfRanges()} form.
413   */
414  private static class SerializedForm<K extends Comparable<?>, V> implements Serializable {
415
416    private final ImmutableMap<Range<K>, V> mapOfRanges;
417
418    SerializedForm(ImmutableMap<Range<K>, V> mapOfRanges) {
419      this.mapOfRanges = mapOfRanges;
420    }
421
422    Object readResolve() {
423      if (mapOfRanges.isEmpty()) {
424        return of();
425      } else {
426        return createRangeMap();
427      }
428    }
429
430    Object createRangeMap() {
431      Builder<K, V> builder = new Builder<>();
432      for (Entry<Range<K>, V> entry : mapOfRanges.entrySet()) {
433        builder.put(entry.getKey(), entry.getValue());
434      }
435      return builder.build();
436    }
437
438    private static final long serialVersionUID = 0;
439  }
440
441  Object writeReplace() {
442    return new SerializedForm<>(asMapOfRanges());
443  }
444
445  @J2ktIncompatible // java.io.ObjectInputStream
446  private void readObject(ObjectInputStream stream) throws InvalidObjectException {
447    throw new InvalidObjectException("Use SerializedForm");
448  }
449
450  private static final long serialVersionUID = 0;
451}