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