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