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