Coverage Summary for Class: ImmutableRangeSet (com.google.common.collect)

Class Method, % Line, %
ImmutableRangeSet 0% (0/34) 0% (0/128)
ImmutableRangeSet$1 0% (0/4) 0% (0/7)
ImmutableRangeSet$AsSet 0% (0/15) 0% (0/45)
ImmutableRangeSet$AsSet$1 0% (0/2) 0% (0/8)
ImmutableRangeSet$AsSet$2 0% (0/2) 0% (0/8)
ImmutableRangeSet$AsSetSerializedForm 0% (0/2) 0% (0/4)
ImmutableRangeSet$Builder 0% (0/6) 0% (0/34)
ImmutableRangeSet$ComplementRanges 0% (0/4) 0% (0/19)
ImmutableRangeSet$SerializedForm 0% (0/2) 0% (0/7)
Total 0% (0/71) 0% (0/260)


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