• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.collect;
16 
17 import static com.google.common.base.Preconditions.checkArgument;
18 import static com.google.common.base.Preconditions.checkElementIndex;
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.annotations.Beta;
22 import com.google.common.annotations.GwtIncompatible;
23 import com.google.common.collect.SortedLists.KeyAbsentBehavior;
24 import com.google.common.collect.SortedLists.KeyPresentBehavior;
25 import com.google.errorprone.annotations.CanIgnoreReturnValue;
26 import com.google.errorprone.annotations.DoNotCall;
27 import com.google.errorprone.annotations.DoNotMock;
28 import java.io.Serializable;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Map.Entry;
33 import java.util.NoSuchElementException;
34 import java.util.function.BiFunction;
35 import java.util.function.Function;
36 import java.util.stream.Collector;
37 import javax.annotation.CheckForNull;
38 import org.checkerframework.checker.nullness.qual.Nullable;
39 
40 /**
41  * A {@link RangeMap} whose contents will never change, with many other important properties
42  * detailed at {@link ImmutableCollection}.
43  *
44  * @author Louis Wasserman
45  * @since 14.0
46  */
47 @Beta
48 @GwtIncompatible // NavigableMap
49 @ElementTypesAreNonnullByDefault
50 public class ImmutableRangeMap<K extends Comparable<?>, V> implements RangeMap<K, V>, Serializable {
51 
52   private static final ImmutableRangeMap<Comparable<?>, Object> EMPTY =
53       new ImmutableRangeMap<>(ImmutableList.<Range<Comparable<?>>>of(), ImmutableList.of());
54 
55   /**
56    * Returns a {@code Collector} that accumulates the input elements into a new {@code
57    * ImmutableRangeMap}. As in {@link Builder}, overlapping ranges are not permitted.
58    *
59    * @since 23.1
60    */
61   public static <T extends @Nullable Object, K extends Comparable<? super K>, V>
toImmutableRangeMap( Function<? super T, Range<K>> keyFunction, Function<? super T, ? extends V> valueFunction)62       Collector<T, ?, ImmutableRangeMap<K, V>> toImmutableRangeMap(
63           Function<? super T, Range<K>> keyFunction,
64           Function<? super T, ? extends V> valueFunction) {
65     return CollectCollectors.toImmutableRangeMap(keyFunction, valueFunction);
66   }
67 
68   /**
69    * Returns an empty immutable range map.
70    *
71    * <p><b>Performance note:</b> the instance returned is a singleton.
72    */
73   @SuppressWarnings("unchecked")
of()74   public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of() {
75     return (ImmutableRangeMap<K, V>) EMPTY;
76   }
77 
78   /** Returns an immutable range map mapping a single range to a single value. */
of(Range<K> range, V value)79   public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of(Range<K> range, V value) {
80     return new ImmutableRangeMap<>(ImmutableList.of(range), ImmutableList.of(value));
81   }
82 
83   @SuppressWarnings("unchecked")
copyOf( RangeMap<K, ? extends V> rangeMap)84   public static <K extends Comparable<?>, V> ImmutableRangeMap<K, V> copyOf(
85       RangeMap<K, ? extends V> rangeMap) {
86     if (rangeMap instanceof ImmutableRangeMap) {
87       return (ImmutableRangeMap<K, V>) rangeMap;
88     }
89     Map<Range<K>, ? extends V> map = rangeMap.asMapOfRanges();
90     ImmutableList.Builder<Range<K>> rangesBuilder = new ImmutableList.Builder<>(map.size());
91     ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<V>(map.size());
92     for (Entry<Range<K>, ? extends V> entry : map.entrySet()) {
93       rangesBuilder.add(entry.getKey());
94       valuesBuilder.add(entry.getValue());
95     }
96     return new ImmutableRangeMap<>(rangesBuilder.build(), valuesBuilder.build());
97   }
98 
99   /** Returns a new builder for an immutable range map. */
builder()100   public static <K extends Comparable<?>, V> Builder<K, V> builder() {
101     return new Builder<>();
102   }
103 
104   /**
105    * A builder for immutable range maps. Overlapping ranges are prohibited.
106    *
107    * @since 14.0
108    */
109   @DoNotMock
110   public static final class Builder<K extends Comparable<?>, V> {
111     private final List<Entry<Range<K>, V>> entries;
112 
Builder()113     public Builder() {
114       this.entries = Lists.newArrayList();
115     }
116 
117     /**
118      * Associates the specified range with the specified value.
119      *
120      * @throws IllegalArgumentException if {@code range} is empty
121      */
122     @CanIgnoreReturnValue
put(Range<K> range, V value)123     public Builder<K, V> put(Range<K> range, V value) {
124       checkNotNull(range);
125       checkNotNull(value);
126       checkArgument(!range.isEmpty(), "Range must not be empty, but was %s", range);
127       entries.add(Maps.immutableEntry(range, value));
128       return this;
129     }
130 
131     /** Copies all associations from the specified range map into this builder. */
132     @CanIgnoreReturnValue
putAll(RangeMap<K, ? extends V> rangeMap)133     public Builder<K, V> putAll(RangeMap<K, ? extends V> rangeMap) {
134       for (Entry<Range<K>, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) {
135         put(entry.getKey(), entry.getValue());
136       }
137       return this;
138     }
139 
140     @CanIgnoreReturnValue
combine(Builder<K, V> builder)141     Builder<K, V> combine(Builder<K, V> builder) {
142       entries.addAll(builder.entries);
143       return this;
144     }
145 
146     /**
147      * Returns an {@code ImmutableRangeMap} containing the associations previously added to this
148      * builder.
149      *
150      * @throws IllegalArgumentException if any two ranges inserted into this builder overlap
151      */
build()152     public ImmutableRangeMap<K, V> build() {
153       Collections.sort(entries, Range.<K>rangeLexOrdering().onKeys());
154       ImmutableList.Builder<Range<K>> rangesBuilder = new ImmutableList.Builder<>(entries.size());
155       ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<V>(entries.size());
156       for (int i = 0; i < entries.size(); i++) {
157         Range<K> range = entries.get(i).getKey();
158         if (i > 0) {
159           Range<K> prevRange = entries.get(i - 1).getKey();
160           if (range.isConnected(prevRange) && !range.intersection(prevRange).isEmpty()) {
161             throw new IllegalArgumentException(
162                 "Overlapping ranges: range " + prevRange + " overlaps with entry " + range);
163           }
164         }
165         rangesBuilder.add(range);
166         valuesBuilder.add(entries.get(i).getValue());
167       }
168       return new ImmutableRangeMap<>(rangesBuilder.build(), valuesBuilder.build());
169     }
170   }
171 
172   private final transient ImmutableList<Range<K>> ranges;
173   private final transient ImmutableList<V> values;
174 
ImmutableRangeMap(ImmutableList<Range<K>> ranges, ImmutableList<V> values)175   ImmutableRangeMap(ImmutableList<Range<K>> ranges, ImmutableList<V> values) {
176     this.ranges = ranges;
177     this.values = values;
178   }
179 
180   @Override
181   @CheckForNull
get(K key)182   public V get(K key) {
183     int index =
184         SortedLists.binarySearch(
185             ranges,
186             Range.<K>lowerBoundFn(),
187             Cut.belowValue(key),
188             KeyPresentBehavior.ANY_PRESENT,
189             KeyAbsentBehavior.NEXT_LOWER);
190     if (index == -1) {
191       return null;
192     } else {
193       Range<K> range = ranges.get(index);
194       return range.contains(key) ? values.get(index) : null;
195     }
196   }
197 
198   @Override
199   @CheckForNull
getEntry(K key)200   public Entry<Range<K>, V> getEntry(K key) {
201     int index =
202         SortedLists.binarySearch(
203             ranges,
204             Range.<K>lowerBoundFn(),
205             Cut.belowValue(key),
206             KeyPresentBehavior.ANY_PRESENT,
207             KeyAbsentBehavior.NEXT_LOWER);
208     if (index == -1) {
209       return null;
210     } else {
211       Range<K> range = ranges.get(index);
212       return range.contains(key) ? Maps.immutableEntry(range, values.get(index)) : null;
213     }
214   }
215 
216   @Override
span()217   public Range<K> span() {
218     if (ranges.isEmpty()) {
219       throw new NoSuchElementException();
220     }
221     Range<K> firstRange = ranges.get(0);
222     Range<K> lastRange = ranges.get(ranges.size() - 1);
223     return Range.create(firstRange.lowerBound, lastRange.upperBound);
224   }
225 
226   /**
227    * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
228    *
229    * @throws UnsupportedOperationException always
230    * @deprecated Unsupported operation.
231    */
232   @Deprecated
233   @Override
234   @DoNotCall("Always throws UnsupportedOperationException")
put(Range<K> range, V value)235   public final void put(Range<K> range, V value) {
236     throw new UnsupportedOperationException();
237   }
238 
239   /**
240    * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
241    *
242    * @throws UnsupportedOperationException always
243    * @deprecated Unsupported operation.
244    */
245   @Deprecated
246   @Override
247   @DoNotCall("Always throws UnsupportedOperationException")
putCoalescing(Range<K> range, V value)248   public final void putCoalescing(Range<K> range, V value) {
249     throw new UnsupportedOperationException();
250   }
251 
252   /**
253    * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
254    *
255    * @throws UnsupportedOperationException always
256    * @deprecated Unsupported operation.
257    */
258   @Deprecated
259   @Override
260   @DoNotCall("Always throws UnsupportedOperationException")
putAll(RangeMap<K, V> rangeMap)261   public final void putAll(RangeMap<K, V> rangeMap) {
262     throw new UnsupportedOperationException();
263   }
264 
265   /**
266    * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
267    *
268    * @throws UnsupportedOperationException always
269    * @deprecated Unsupported operation.
270    */
271   @Deprecated
272   @Override
273   @DoNotCall("Always throws UnsupportedOperationException")
clear()274   public final void clear() {
275     throw new UnsupportedOperationException();
276   }
277 
278   /**
279    * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
280    *
281    * @throws UnsupportedOperationException always
282    * @deprecated Unsupported operation.
283    */
284   @Deprecated
285   @Override
286   @DoNotCall("Always throws UnsupportedOperationException")
remove(Range<K> range)287   public final void remove(Range<K> range) {
288     throw new UnsupportedOperationException();
289   }
290 
291   /**
292    * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified.
293    *
294    * @throws UnsupportedOperationException always
295    * @deprecated Unsupported operation.
296    */
297   @Deprecated
298   @Override
299   @DoNotCall("Always throws UnsupportedOperationException")
merge( Range<K> range, @CheckForNull V value, BiFunction<? super V, ? super @Nullable V, ? extends @Nullable V> remappingFunction)300   public final void merge(
301       Range<K> range,
302       @CheckForNull V value,
303       BiFunction<? super V, ? super @Nullable V, ? extends @Nullable V> remappingFunction) {
304     throw new UnsupportedOperationException();
305   }
306 
307   @Override
asMapOfRanges()308   public ImmutableMap<Range<K>, V> asMapOfRanges() {
309     if (ranges.isEmpty()) {
310       return ImmutableMap.of();
311     }
312     RegularImmutableSortedSet<Range<K>> rangeSet =
313         new RegularImmutableSortedSet<>(ranges, Range.<K>rangeLexOrdering());
314     return new ImmutableSortedMap<>(rangeSet, values);
315   }
316 
317   @Override
asDescendingMapOfRanges()318   public ImmutableMap<Range<K>, V> asDescendingMapOfRanges() {
319     if (ranges.isEmpty()) {
320       return ImmutableMap.of();
321     }
322     RegularImmutableSortedSet<Range<K>> rangeSet =
323         new RegularImmutableSortedSet<>(ranges.reverse(), Range.<K>rangeLexOrdering().reverse());
324     return new ImmutableSortedMap<>(rangeSet, values.reverse());
325   }
326 
327   @Override
subRangeMap(final Range<K> range)328   public ImmutableRangeMap<K, V> subRangeMap(final Range<K> range) {
329     if (checkNotNull(range).isEmpty()) {
330       return ImmutableRangeMap.of();
331     } else if (ranges.isEmpty() || range.encloses(span())) {
332       return this;
333     }
334     int lowerIndex =
335         SortedLists.binarySearch(
336             ranges,
337             Range.<K>upperBoundFn(),
338             range.lowerBound,
339             KeyPresentBehavior.FIRST_AFTER,
340             KeyAbsentBehavior.NEXT_HIGHER);
341     int upperIndex =
342         SortedLists.binarySearch(
343             ranges,
344             Range.<K>lowerBoundFn(),
345             range.upperBound,
346             KeyPresentBehavior.ANY_PRESENT,
347             KeyAbsentBehavior.NEXT_HIGHER);
348     if (lowerIndex >= upperIndex) {
349       return ImmutableRangeMap.of();
350     }
351     final int off = lowerIndex;
352     final int len = upperIndex - lowerIndex;
353     ImmutableList<Range<K>> subRanges =
354         new ImmutableList<Range<K>>() {
355           @Override
356           public int size() {
357             return len;
358           }
359 
360           @Override
361           public Range<K> get(int index) {
362             checkElementIndex(index, len);
363             if (index == 0 || index == len - 1) {
364               return ranges.get(index + off).intersection(range);
365             } else {
366               return ranges.get(index + off);
367             }
368           }
369 
370           @Override
371           boolean isPartialView() {
372             return true;
373           }
374         };
375     final ImmutableRangeMap<K, V> outer = this;
376     return new ImmutableRangeMap<K, V>(subRanges, values.subList(lowerIndex, upperIndex)) {
377       @Override
378       public ImmutableRangeMap<K, V> subRangeMap(Range<K> subRange) {
379         if (range.isConnected(subRange)) {
380           return outer.subRangeMap(subRange.intersection(range));
381         } else {
382           return ImmutableRangeMap.of();
383         }
384       }
385     };
386   }
387 
388   @Override
389   public int hashCode() {
390     return asMapOfRanges().hashCode();
391   }
392 
393   @Override
394   public boolean equals(@CheckForNull Object o) {
395     if (o instanceof RangeMap) {
396       RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o;
397       return asMapOfRanges().equals(rangeMap.asMapOfRanges());
398     }
399     return false;
400   }
401 
402   @Override
403   public String toString() {
404     return asMapOfRanges().toString();
405   }
406 
407   /**
408    * This class is used to serialize ImmutableRangeMap instances. Serializes the {@link
409    * #asMapOfRanges()} form.
410    */
411   private static class SerializedForm<K extends Comparable<?>, V> implements Serializable {
412 
413     private final ImmutableMap<Range<K>, V> mapOfRanges;
414 
415     SerializedForm(ImmutableMap<Range<K>, V> mapOfRanges) {
416       this.mapOfRanges = mapOfRanges;
417     }
418 
419     Object readResolve() {
420       if (mapOfRanges.isEmpty()) {
421         return of();
422       } else {
423         return createRangeMap();
424       }
425     }
426 
427     Object createRangeMap() {
428       Builder<K, V> builder = new Builder<>();
429       for (Entry<Range<K>, V> entry : mapOfRanges.entrySet()) {
430         builder.put(entry.getKey(), entry.getValue());
431       }
432       return builder.build();
433     }
434 
435     private static final long serialVersionUID = 0;
436   }
437 
438   Object writeReplace() {
439     return new SerializedForm<>(asMapOfRanges());
440   }
441 
442   private static final long serialVersionUID = 0;
443 }
444