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.SortedLists.KeyAbsentBehavior.NEXT_HIGHER;
021import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER;
022import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT;
023
024import com.google.common.annotations.Beta;
025import com.google.common.annotations.GwtIncompatible;
026import com.google.common.collect.SortedLists.KeyAbsentBehavior;
027import com.google.common.collect.SortedLists.KeyPresentBehavior;
028import com.google.common.primitives.Ints;
029import com.google.errorprone.annotations.CanIgnoreReturnValue;
030import com.google.errorprone.annotations.concurrent.LazyInit;
031import java.io.Serializable;
032import java.util.Collections;
033import java.util.Iterator;
034import java.util.List;
035import java.util.NoSuchElementException;
036import java.util.Set;
037import java.util.stream.Collector;
038import org.checkerframework.checker.nullness.qual.Nullable;
039
040/**
041 * A {@link RangeSet} whose contents will never change, with many other important properties
042 * detailed at {@link ImmutableCollection}.
043 *
044 * @author Louis Wasserman
045 * @since 14.0
046 */
047@Beta
048@GwtIncompatible
049public final class ImmutableRangeSet<C extends Comparable> extends AbstractRangeSet<C>
050    implements Serializable {
051
052  private static final ImmutableRangeSet<Comparable<?>> EMPTY =
053      new ImmutableRangeSet<>(ImmutableList.<Range<Comparable<?>>>of());
054
055  private static final ImmutableRangeSet<Comparable<?>> ALL =
056      new ImmutableRangeSet<>(ImmutableList.of(Range.<Comparable<?>>all()));
057
058  /**
059   * Returns a {@code Collector} that accumulates the input elements into a new {@code
060   * ImmutableRangeSet}. As in {@link Builder}, overlapping ranges are not permitted and adjacent
061   * ranges will be merged.
062   *
063   * @since 23.1
064   */
065  public static <E extends Comparable<? super E>>
066      Collector<Range<E>, ?, ImmutableRangeSet<E>> toImmutableRangeSet() {
067    return CollectCollectors.toImmutableRangeSet();
068  }
069
070  /** Returns an empty immutable range set. */
071  @SuppressWarnings("unchecked")
072  public static <C extends Comparable> ImmutableRangeSet<C> of() {
073    return (ImmutableRangeSet<C>) EMPTY;
074  }
075
076  /**
077   * Returns an immutable range set containing the specified single range. If {@link Range#isEmpty()
078   * range.isEmpty()}, this is equivalent to {@link ImmutableRangeSet#of()}.
079   */
080  public static <C extends Comparable> ImmutableRangeSet<C> of(Range<C> range) {
081    checkNotNull(range);
082    if (range.isEmpty()) {
083      return of();
084    } else if (range.equals(Range.all())) {
085      return all();
086    } else {
087      return new ImmutableRangeSet<C>(ImmutableList.of(range));
088    }
089  }
090
091  /** Returns an immutable range set containing the single range {@link Range#all()}. */
092  @SuppressWarnings("unchecked")
093  static <C extends Comparable> ImmutableRangeSet<C> all() {
094    return (ImmutableRangeSet<C>) ALL;
095  }
096
097  /** Returns an immutable copy of the specified {@code RangeSet}. */
098  public static <C extends Comparable> ImmutableRangeSet<C> copyOf(RangeSet<C> rangeSet) {
099    checkNotNull(rangeSet);
100    if (rangeSet.isEmpty()) {
101      return of();
102    } else if (rangeSet.encloses(Range.<C>all())) {
103      return all();
104    }
105
106    if (rangeSet instanceof ImmutableRangeSet) {
107      ImmutableRangeSet<C> immutableRangeSet = (ImmutableRangeSet<C>) rangeSet;
108      if (!immutableRangeSet.isPartialView()) {
109        return immutableRangeSet;
110      }
111    }
112    return new ImmutableRangeSet<C>(ImmutableList.copyOf(rangeSet.asRanges()));
113  }
114
115  /**
116   * Returns an {@code ImmutableRangeSet} containing each of the specified disjoint ranges.
117   * Overlapping ranges and empty ranges are forbidden, though adjacent ranges are permitted and
118   * will be merged.
119   *
120   * @throws IllegalArgumentException if any ranges overlap or are empty
121   * @since 21.0
122   */
123  public static <C extends Comparable<?>> ImmutableRangeSet<C> copyOf(Iterable<Range<C>> ranges) {
124    return new ImmutableRangeSet.Builder<C>().addAll(ranges).build();
125  }
126
127  /**
128   * Returns an {@code ImmutableRangeSet} representing the union of the specified ranges.
129   *
130   * <p>This is the smallest {@code RangeSet} which encloses each of the specified ranges. Duplicate
131   * or connected ranges are permitted, and will be coalesced in the result.
132   *
133   * @since 21.0
134   */
135  public static <C extends Comparable<?>> ImmutableRangeSet<C> unionOf(Iterable<Range<C>> ranges) {
136    return copyOf(TreeRangeSet.create(ranges));
137  }
138
139  ImmutableRangeSet(ImmutableList<Range<C>> ranges) {
140    this.ranges = ranges;
141  }
142
143  private ImmutableRangeSet(ImmutableList<Range<C>> ranges, ImmutableRangeSet<C> complement) {
144    this.ranges = ranges;
145    this.complement = complement;
146  }
147
148  private final transient ImmutableList<Range<C>> ranges;
149
150  @Override
151  public boolean intersects(Range<C> otherRange) {
152    int ceilingIndex =
153        SortedLists.binarySearch(
154            ranges,
155            Range.<C>lowerBoundFn(),
156            otherRange.lowerBound,
157            Ordering.natural(),
158            ANY_PRESENT,
159            NEXT_HIGHER);
160    if (ceilingIndex < ranges.size()
161        && ranges.get(ceilingIndex).isConnected(otherRange)
162        && !ranges.get(ceilingIndex).intersection(otherRange).isEmpty()) {
163      return true;
164    }
165    return ceilingIndex > 0
166        && ranges.get(ceilingIndex - 1).isConnected(otherRange)
167        && !ranges.get(ceilingIndex - 1).intersection(otherRange).isEmpty();
168  }
169
170  @Override
171  public boolean encloses(Range<C> otherRange) {
172    int index =
173        SortedLists.binarySearch(
174            ranges,
175            Range.<C>lowerBoundFn(),
176            otherRange.lowerBound,
177            Ordering.natural(),
178            ANY_PRESENT,
179            NEXT_LOWER);
180    return index != -1 && ranges.get(index).encloses(otherRange);
181  }
182
183  @Override
184  public Range<C> rangeContaining(C value) {
185    int index =
186        SortedLists.binarySearch(
187            ranges,
188            Range.<C>lowerBoundFn(),
189            Cut.belowValue(value),
190            Ordering.natural(),
191            ANY_PRESENT,
192            NEXT_LOWER);
193    if (index != -1) {
194      Range<C> range = ranges.get(index);
195      return range.contains(value) ? range : null;
196    }
197    return null;
198  }
199
200  @Override
201  public Range<C> span() {
202    if (ranges.isEmpty()) {
203      throw new NoSuchElementException();
204    }
205    return Range.create(ranges.get(0).lowerBound, ranges.get(ranges.size() - 1).upperBound);
206  }
207
208  @Override
209  public boolean isEmpty() {
210    return ranges.isEmpty();
211  }
212
213  /**
214   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
215   *
216   * @throws UnsupportedOperationException always
217   * @deprecated Unsupported operation.
218   */
219  @Deprecated
220  @Override
221  public void add(Range<C> range) {
222    throw new UnsupportedOperationException();
223  }
224
225  /**
226   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
227   *
228   * @throws UnsupportedOperationException always
229   * @deprecated Unsupported operation.
230   */
231  @Deprecated
232  @Override
233  public void addAll(RangeSet<C> other) {
234    throw new UnsupportedOperationException();
235  }
236
237  /**
238   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
239   *
240   * @throws UnsupportedOperationException always
241   * @deprecated Unsupported operation.
242   */
243  @Deprecated
244  @Override
245  public void addAll(Iterable<Range<C>> other) {
246    throw new UnsupportedOperationException();
247  }
248
249  /**
250   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
251   *
252   * @throws UnsupportedOperationException always
253   * @deprecated Unsupported operation.
254   */
255  @Deprecated
256  @Override
257  public void remove(Range<C> range) {
258    throw new UnsupportedOperationException();
259  }
260
261  /**
262   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
263   *
264   * @throws UnsupportedOperationException always
265   * @deprecated Unsupported operation.
266   */
267  @Deprecated
268  @Override
269  public void removeAll(RangeSet<C> other) {
270    throw new UnsupportedOperationException();
271  }
272
273  /**
274   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
275   *
276   * @throws UnsupportedOperationException always
277   * @deprecated Unsupported operation.
278   */
279  @Deprecated
280  @Override
281  public void removeAll(Iterable<Range<C>> other) {
282    throw new UnsupportedOperationException();
283  }
284
285  @Override
286  public ImmutableSet<Range<C>> asRanges() {
287    if (ranges.isEmpty()) {
288      return ImmutableSet.of();
289    }
290    return new RegularImmutableSortedSet<>(ranges, Range.<C>rangeLexOrdering());
291  }
292
293  @Override
294  public ImmutableSet<Range<C>> asDescendingSetOfRanges() {
295    if (ranges.isEmpty()) {
296      return ImmutableSet.of();
297    }
298    return new RegularImmutableSortedSet<>(ranges.reverse(), Range.<C>rangeLexOrdering().reverse());
299  }
300
301  @LazyInit private transient ImmutableRangeSet<C> complement;
302
303  private final class ComplementRanges extends ImmutableList<Range<C>> {
304    // True if the "positive" range set is empty or bounded below.
305    private final boolean positiveBoundedBelow;
306
307    // True if the "positive" range set is empty or bounded above.
308    private final boolean positiveBoundedAbove;
309
310    private final int size;
311
312    ComplementRanges() {
313      this.positiveBoundedBelow = ranges.get(0).hasLowerBound();
314      this.positiveBoundedAbove = Iterables.getLast(ranges).hasUpperBound();
315
316      int size = ranges.size() - 1;
317      if (positiveBoundedBelow) {
318        size++;
319      }
320      if (positiveBoundedAbove) {
321        size++;
322      }
323      this.size = size;
324    }
325
326    @Override
327    public int size() {
328      return size;
329    }
330
331    @Override
332    public Range<C> get(int index) {
333      checkElementIndex(index, size);
334
335      Cut<C> lowerBound;
336      if (positiveBoundedBelow) {
337        lowerBound = (index == 0) ? Cut.<C>belowAll() : ranges.get(index - 1).upperBound;
338      } else {
339        lowerBound = ranges.get(index).upperBound;
340      }
341
342      Cut<C> upperBound;
343      if (positiveBoundedAbove && index == size - 1) {
344        upperBound = Cut.<C>aboveAll();
345      } else {
346        upperBound = ranges.get(index + (positiveBoundedBelow ? 0 : 1)).lowerBound;
347      }
348
349      return Range.create(lowerBound, upperBound);
350    }
351
352    @Override
353    boolean isPartialView() {
354      return true;
355    }
356  }
357
358  @Override
359  public ImmutableRangeSet<C> complement() {
360    ImmutableRangeSet<C> result = complement;
361    if (result != null) {
362      return result;
363    } else if (ranges.isEmpty()) {
364      return complement = all();
365    } else if (ranges.size() == 1 && ranges.get(0).equals(Range.all())) {
366      return complement = of();
367    } else {
368      ImmutableList<Range<C>> complementRanges = new ComplementRanges();
369      result = complement = new ImmutableRangeSet<C>(complementRanges, this);
370    }
371    return result;
372  }
373
374  /**
375   * Returns a new range set consisting of the union of this range set and {@code other}.
376   *
377   * <p>This is essentially the same as {@code TreeRangeSet.create(this).addAll(other)} except it
378   * returns an {@code ImmutableRangeSet}.
379   *
380   * @since 21.0
381   */
382  public ImmutableRangeSet<C> union(RangeSet<C> other) {
383    return unionOf(Iterables.concat(asRanges(), other.asRanges()));
384  }
385
386  /**
387   * Returns a new range set consisting of the intersection of this range set and {@code other}.
388   *
389   * <p>This is essentially the same as {@code
390   * TreeRangeSet.create(this).removeAll(other.complement())} except it returns an {@code
391   * ImmutableRangeSet}.
392   *
393   * @since 21.0
394   */
395  public ImmutableRangeSet<C> intersection(RangeSet<C> other) {
396    RangeSet<C> copy = TreeRangeSet.create(this);
397    copy.removeAll(other.complement());
398    return copyOf(copy);
399  }
400
401  /**
402   * Returns a new range set consisting of the difference of this range set and {@code other}.
403   *
404   * <p>This is essentially the same as {@code TreeRangeSet.create(this).removeAll(other)} except it
405   * returns an {@code ImmutableRangeSet}.
406   *
407   * @since 21.0
408   */
409  public ImmutableRangeSet<C> difference(RangeSet<C> other) {
410    RangeSet<C> copy = TreeRangeSet.create(this);
411    copy.removeAll(other);
412    return copyOf(copy);
413  }
414
415  /**
416   * Returns a list containing the nonempty intersections of {@code range} with the ranges in this
417   * range set.
418   */
419  private ImmutableList<Range<C>> intersectRanges(final Range<C> range) {
420    if (ranges.isEmpty() || range.isEmpty()) {
421      return ImmutableList.of();
422    } else if (range.encloses(span())) {
423      return ranges;
424    }
425
426    final int fromIndex;
427    if (range.hasLowerBound()) {
428      fromIndex =
429          SortedLists.binarySearch(
430              ranges,
431              Range.<C>upperBoundFn(),
432              range.lowerBound,
433              KeyPresentBehavior.FIRST_AFTER,
434              KeyAbsentBehavior.NEXT_HIGHER);
435    } else {
436      fromIndex = 0;
437    }
438
439    int toIndex;
440    if (range.hasUpperBound()) {
441      toIndex =
442          SortedLists.binarySearch(
443              ranges,
444              Range.<C>lowerBoundFn(),
445              range.upperBound,
446              KeyPresentBehavior.FIRST_PRESENT,
447              KeyAbsentBehavior.NEXT_HIGHER);
448    } else {
449      toIndex = ranges.size();
450    }
451    final int length = toIndex - fromIndex;
452    if (length == 0) {
453      return ImmutableList.of();
454    } else {
455      return new ImmutableList<Range<C>>() {
456        @Override
457        public int size() {
458          return length;
459        }
460
461        @Override
462        public Range<C> get(int index) {
463          checkElementIndex(index, length);
464          if (index == 0 || index == length - 1) {
465            return ranges.get(index + fromIndex).intersection(range);
466          } else {
467            return ranges.get(index + fromIndex);
468          }
469        }
470
471        @Override
472        boolean isPartialView() {
473          return true;
474        }
475      };
476    }
477  }
478
479  /** Returns a view of the intersection of this range set with the given range. */
480  @Override
481  public ImmutableRangeSet<C> subRangeSet(Range<C> range) {
482    if (!isEmpty()) {
483      Range<C> span = span();
484      if (range.encloses(span)) {
485        return this;
486      } else if (range.isConnected(span)) {
487        return new ImmutableRangeSet<C>(intersectRanges(range));
488      }
489    }
490    return of();
491  }
492
493  /**
494   * Returns an {@link ImmutableSortedSet} containing the same values in the given domain
495   * {@linkplain RangeSet#contains contained} by this range set.
496   *
497   * <p><b>Note:</b> {@code a.asSet(d).equals(b.asSet(d))} does not imply {@code a.equals(b)}! For
498   * example, {@code a} and {@code b} could be {@code [2..4]} and {@code (1..5)}, or the empty
499   * ranges {@code [3..3)} and {@code [4..4)}.
500   *
501   * <p><b>Warning:</b> Be extremely careful what you do with the {@code asSet} view of a large
502   * range set (such as {@code ImmutableRangeSet.of(Range.greaterThan(0))}). Certain operations on
503   * such a set can be performed efficiently, but others (such as {@link Set#hashCode} or {@link
504   * Collections#frequency}) can cause major performance problems.
505   *
506   * <p>The returned set's {@link Object#toString} method returns a short-hand form of the set's
507   * contents, such as {@code "[1..100]}"}.
508   *
509   * @throws IllegalArgumentException if neither this range nor the domain has a lower bound, or if
510   *     neither has an upper bound
511   */
512  public ImmutableSortedSet<C> asSet(DiscreteDomain<C> domain) {
513    checkNotNull(domain);
514    if (isEmpty()) {
515      return ImmutableSortedSet.of();
516    }
517    Range<C> span = span().canonical(domain);
518    if (!span.hasLowerBound()) {
519      // according to the spec of canonical, neither this ImmutableRangeSet nor
520      // the range have a lower bound
521      throw new IllegalArgumentException(
522          "Neither the DiscreteDomain nor this range set are bounded below");
523    } else if (!span.hasUpperBound()) {
524      try {
525        domain.maxValue();
526      } catch (NoSuchElementException e) {
527        throw new IllegalArgumentException(
528            "Neither the DiscreteDomain nor this range set are bounded above");
529      }
530    }
531
532    return new AsSet(domain);
533  }
534
535  private final class AsSet extends ImmutableSortedSet<C> {
536    private final DiscreteDomain<C> domain;
537
538    AsSet(DiscreteDomain<C> domain) {
539      super(Ordering.natural());
540      this.domain = domain;
541    }
542
543    private transient @Nullable Integer size;
544
545    @Override
546    public int size() {
547      // racy single-check idiom
548      Integer result = size;
549      if (result == null) {
550        long total = 0;
551        for (Range<C> range : ranges) {
552          total += ContiguousSet.create(range, domain).size();
553          if (total >= Integer.MAX_VALUE) {
554            break;
555          }
556        }
557        result = size = Ints.saturatedCast(total);
558      }
559      return result.intValue();
560    }
561
562    @Override
563    public UnmodifiableIterator<C> iterator() {
564      return new AbstractIterator<C>() {
565        final Iterator<Range<C>> rangeItr = ranges.iterator();
566        Iterator<C> elemItr = Iterators.emptyIterator();
567
568        @Override
569        protected C computeNext() {
570          while (!elemItr.hasNext()) {
571            if (rangeItr.hasNext()) {
572              elemItr = ContiguousSet.create(rangeItr.next(), domain).iterator();
573            } else {
574              return endOfData();
575            }
576          }
577          return elemItr.next();
578        }
579      };
580    }
581
582    @Override
583    @GwtIncompatible("NavigableSet")
584    public UnmodifiableIterator<C> descendingIterator() {
585      return new AbstractIterator<C>() {
586        final Iterator<Range<C>> rangeItr = ranges.reverse().iterator();
587        Iterator<C> elemItr = Iterators.emptyIterator();
588
589        @Override
590        protected C computeNext() {
591          while (!elemItr.hasNext()) {
592            if (rangeItr.hasNext()) {
593              elemItr = ContiguousSet.create(rangeItr.next(), domain).descendingIterator();
594            } else {
595              return endOfData();
596            }
597          }
598          return elemItr.next();
599        }
600      };
601    }
602
603    ImmutableSortedSet<C> subSet(Range<C> range) {
604      return subRangeSet(range).asSet(domain);
605    }
606
607    @Override
608    ImmutableSortedSet<C> headSetImpl(C toElement, boolean inclusive) {
609      return subSet(Range.upTo(toElement, BoundType.forBoolean(inclusive)));
610    }
611
612    @Override
613    ImmutableSortedSet<C> subSetImpl(
614        C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) {
615      if (!fromInclusive && !toInclusive && Range.compareOrThrow(fromElement, toElement) == 0) {
616        return ImmutableSortedSet.of();
617      }
618      return subSet(
619          Range.range(
620              fromElement, BoundType.forBoolean(fromInclusive),
621              toElement, BoundType.forBoolean(toInclusive)));
622    }
623
624    @Override
625    ImmutableSortedSet<C> tailSetImpl(C fromElement, boolean inclusive) {
626      return subSet(Range.downTo(fromElement, BoundType.forBoolean(inclusive)));
627    }
628
629    @Override
630    public boolean contains(@Nullable Object o) {
631      if (o == null) {
632        return false;
633      }
634      try {
635        @SuppressWarnings("unchecked") // we catch CCE's
636        C c = (C) o;
637        return ImmutableRangeSet.this.contains(c);
638      } catch (ClassCastException e) {
639        return false;
640      }
641    }
642
643    @Override
644    int indexOf(Object target) {
645      if (contains(target)) {
646        @SuppressWarnings("unchecked") // if it's contained, it's definitely a C
647        C c = (C) target;
648        long total = 0;
649        for (Range<C> range : ranges) {
650          if (range.contains(c)) {
651            return Ints.saturatedCast(total + ContiguousSet.create(range, domain).indexOf(c));
652          } else {
653            total += ContiguousSet.create(range, domain).size();
654          }
655        }
656        throw new AssertionError("impossible");
657      }
658      return -1;
659    }
660
661    @Override
662    ImmutableSortedSet<C> createDescendingSet() {
663      return new DescendingImmutableSortedSet<C>(this);
664    }
665
666    @Override
667    boolean isPartialView() {
668      return ranges.isPartialView();
669    }
670
671    @Override
672    public String toString() {
673      return ranges.toString();
674    }
675
676    @Override
677    Object writeReplace() {
678      return new AsSetSerializedForm<C>(ranges, domain);
679    }
680  }
681
682  private static class AsSetSerializedForm<C extends Comparable> implements Serializable {
683    private final ImmutableList<Range<C>> ranges;
684    private final DiscreteDomain<C> domain;
685
686    AsSetSerializedForm(ImmutableList<Range<C>> ranges, DiscreteDomain<C> domain) {
687      this.ranges = ranges;
688      this.domain = domain;
689    }
690
691    Object readResolve() {
692      return new ImmutableRangeSet<C>(ranges).asSet(domain);
693    }
694  }
695
696  /**
697   * Returns {@code true} if this immutable range set's implementation contains references to
698   * user-created objects that aren't accessible via this range set's methods. This is generally
699   * used to determine whether {@code copyOf} implementations should make an explicit copy to avoid
700   * memory leaks.
701   */
702  boolean isPartialView() {
703    return ranges.isPartialView();
704  }
705
706  /** Returns a new builder for an immutable range set. */
707  public static <C extends Comparable<?>> Builder<C> builder() {
708    return new Builder<C>();
709  }
710
711  /**
712   * A builder for immutable range sets.
713   *
714   * @since 14.0
715   */
716  public static class Builder<C extends Comparable<?>> {
717    private final List<Range<C>> ranges;
718
719    public Builder() {
720      this.ranges = Lists.newArrayList();
721    }
722
723    // TODO(lowasser): consider adding union, in addition to add, that does allow overlap
724
725    /**
726     * Add the specified range to this builder. Adjacent ranges are permitted and will be merged,
727     * but overlapping ranges will cause an exception when {@link #build()} is called.
728     *
729     * @throws IllegalArgumentException if {@code range} is empty
730     */
731    @CanIgnoreReturnValue
732    public Builder<C> add(Range<C> range) {
733      checkArgument(!range.isEmpty(), "range must not be empty, but was %s", range);
734      ranges.add(range);
735      return this;
736    }
737
738    /**
739     * Add all ranges from the specified range set to this builder. Adjacent ranges are permitted
740     * and will be merged, but overlapping ranges will cause an exception when {@link #build()} is
741     * called.
742     */
743    @CanIgnoreReturnValue
744    public Builder<C> addAll(RangeSet<C> ranges) {
745      return addAll(ranges.asRanges());
746    }
747
748    /**
749     * Add all of the specified ranges to this builder. Adjacent ranges are permitted and will be
750     * merged, but overlapping ranges will cause an exception when {@link #build()} is called.
751     *
752     * @throws IllegalArgumentException if any inserted ranges are empty
753     * @since 21.0
754     */
755    @CanIgnoreReturnValue
756    public Builder<C> addAll(Iterable<Range<C>> ranges) {
757      for (Range<C> range : ranges) {
758        add(range);
759      }
760      return this;
761    }
762
763    @CanIgnoreReturnValue
764    Builder<C> combine(Builder<C> builder) {
765      addAll(builder.ranges);
766      return this;
767    }
768
769    /**
770     * Returns an {@code ImmutableRangeSet} containing the ranges added to this builder.
771     *
772     * @throws IllegalArgumentException if any input ranges have nonempty overlap
773     */
774    public ImmutableRangeSet<C> build() {
775      ImmutableList.Builder<Range<C>> mergedRangesBuilder =
776          new ImmutableList.Builder<>(ranges.size());
777      Collections.sort(ranges, Range.<C>rangeLexOrdering());
778      PeekingIterator<Range<C>> peekingItr = Iterators.peekingIterator(ranges.iterator());
779      while (peekingItr.hasNext()) {
780        Range<C> range = peekingItr.next();
781        while (peekingItr.hasNext()) {
782          Range<C> nextRange = peekingItr.peek();
783          if (range.isConnected(nextRange)) {
784            checkArgument(
785                range.intersection(nextRange).isEmpty(),
786                "Overlapping ranges not permitted but found %s overlapping %s",
787                range,
788                nextRange);
789            range = range.span(peekingItr.next());
790          } else {
791            break;
792          }
793        }
794        mergedRangesBuilder.add(range);
795      }
796      ImmutableList<Range<C>> mergedRanges = mergedRangesBuilder.build();
797      if (mergedRanges.isEmpty()) {
798        return of();
799      } else if (mergedRanges.size() == 1
800          && Iterables.getOnlyElement(mergedRanges).equals(Range.all())) {
801        return all();
802      } else {
803        return new ImmutableRangeSet<C>(mergedRanges);
804      }
805    }
806  }
807
808  private static final class SerializedForm<C extends Comparable> implements Serializable {
809    private final ImmutableList<Range<C>> ranges;
810
811    SerializedForm(ImmutableList<Range<C>> ranges) {
812      this.ranges = ranges;
813    }
814
815    Object readResolve() {
816      if (ranges.isEmpty()) {
817        return of();
818      } else if (ranges.equals(ImmutableList.of(Range.all()))) {
819        return all();
820      } else {
821        return new ImmutableRangeSet<C>(ranges);
822      }
823    }
824  }
825
826  Object writeReplace() {
827    return new SerializedForm<C>(ranges);
828  }
829}