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