001/* 002 * Copyright (C) 2011 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.MoreObjects.firstNonNull; 018import static com.google.common.base.Preconditions.checkArgument; 019import static com.google.common.base.Preconditions.checkNotNull; 020import static com.google.common.collect.Iterators.emptyIterator; 021import static com.google.common.collect.Iterators.peekingIterator; 022import static com.google.common.collect.Maps.immutableEntry; 023import static com.google.common.collect.Maps.valueOrNull; 024import static com.google.common.collect.Sets.equalsImpl; 025import static com.google.common.collect.Sets.hashCodeImpl; 026 027import com.google.common.annotations.GwtIncompatible; 028import com.google.common.annotations.VisibleForTesting; 029import com.google.common.collect.ForwardingCollection.SimpleForwardingCollection; 030import com.google.errorprone.annotations.concurrent.LazyInit; 031import java.io.Serializable; 032import java.util.Collection; 033import java.util.Comparator; 034import java.util.Iterator; 035import java.util.Map.Entry; 036import java.util.NavigableMap; 037import java.util.NoSuchElementException; 038import java.util.Set; 039import java.util.TreeMap; 040import org.jspecify.annotations.Nullable; 041 042/** 043 * An implementation of {@link RangeSet} backed by a {@link TreeMap}. 044 * 045 * @author Louis Wasserman 046 * @since 14.0 047 */ 048@GwtIncompatible // uses NavigableMap 049public class TreeRangeSet<C extends Comparable<?>> extends AbstractRangeSet<C> 050 implements Serializable { 051 052 @VisibleForTesting final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; 053 054 /** Creates an empty {@code TreeRangeSet} instance. */ 055 public static <C extends Comparable<?>> TreeRangeSet<C> create() { 056 return new TreeRangeSet<>(new TreeMap<Cut<C>, Range<C>>()); 057 } 058 059 /** Returns a {@code TreeRangeSet} initialized with the ranges in the specified range set. */ 060 public static <C extends Comparable<?>> TreeRangeSet<C> create(RangeSet<C> rangeSet) { 061 TreeRangeSet<C> result = create(); 062 result.addAll(rangeSet); 063 return result; 064 } 065 066 /** 067 * Returns a {@code TreeRangeSet} representing the union of the specified ranges. 068 * 069 * <p>This is the smallest {@code RangeSet} which encloses each of the specified ranges. An 070 * element will be contained in this {@code RangeSet} if and only if it is contained in at least 071 * one {@code Range} in {@code ranges}. 072 * 073 * @since 21.0 074 */ 075 public static <C extends Comparable<?>> TreeRangeSet<C> create(Iterable<Range<C>> ranges) { 076 TreeRangeSet<C> result = create(); 077 result.addAll(ranges); 078 return result; 079 } 080 081 private TreeRangeSet(NavigableMap<Cut<C>, Range<C>> rangesByLowerCut) { 082 this.rangesByLowerBound = rangesByLowerCut; 083 } 084 085 @Override 086 public Set<Range<C>> asRanges() { 087 return new AsRanges(rangesByLowerBound.values()); 088 } 089 090 @Override 091 public Set<Range<C>> asDescendingSetOfRanges() { 092 return new AsRanges(rangesByLowerBound.descendingMap().values()); 093 } 094 095 final class AsRanges extends SimpleForwardingCollection<Range<C>> implements Set<Range<C>> { 096 AsRanges(Collection<Range<C>> delegate) { 097 super(delegate); 098 } 099 100 @Override 101 public int hashCode() { 102 return hashCodeImpl(this); 103 } 104 105 @Override 106 public boolean equals(@Nullable Object o) { 107 return equalsImpl(this, o); 108 } 109 } 110 111 @Override 112 public @Nullable Range<C> rangeContaining(C value) { 113 checkNotNull(value); 114 Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(Cut.belowValue(value)); 115 if (floorEntry != null && floorEntry.getValue().contains(value)) { 116 return floorEntry.getValue(); 117 } else { 118 // TODO(kevinb): revisit this design choice 119 return null; 120 } 121 } 122 123 @Override 124 public boolean intersects(Range<C> range) { 125 checkNotNull(range); 126 Entry<Cut<C>, Range<C>> ceilingEntry = rangesByLowerBound.ceilingEntry(range.lowerBound); 127 if (ceilingEntry != null 128 && ceilingEntry.getValue().isConnected(range) 129 && !ceilingEntry.getValue().intersection(range).isEmpty()) { 130 return true; 131 } 132 Entry<Cut<C>, Range<C>> priorEntry = rangesByLowerBound.lowerEntry(range.lowerBound); 133 return priorEntry != null 134 && priorEntry.getValue().isConnected(range) 135 && !priorEntry.getValue().intersection(range).isEmpty(); 136 } 137 138 @Override 139 public boolean encloses(Range<C> range) { 140 checkNotNull(range); 141 Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); 142 return floorEntry != null && floorEntry.getValue().encloses(range); 143 } 144 145 private @Nullable Range<C> rangeEnclosing(Range<C> range) { 146 checkNotNull(range); 147 Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); 148 return (floorEntry != null && floorEntry.getValue().encloses(range)) 149 ? floorEntry.getValue() 150 : null; 151 } 152 153 @Override 154 public Range<C> span() { 155 Entry<Cut<C>, Range<C>> firstEntry = rangesByLowerBound.firstEntry(); 156 Entry<Cut<C>, Range<C>> lastEntry = rangesByLowerBound.lastEntry(); 157 if (firstEntry == null || lastEntry == null) { 158 /* 159 * Either both are null or neither is: Either the set is empty, or it's not. But we check both 160 * to make the nullness checker happy. 161 */ 162 throw new NoSuchElementException(); 163 } 164 return Range.create(firstEntry.getValue().lowerBound, lastEntry.getValue().upperBound); 165 } 166 167 @Override 168 public void add(Range<C> rangeToAdd) { 169 checkNotNull(rangeToAdd); 170 171 if (rangeToAdd.isEmpty()) { 172 return; 173 } 174 175 // We will use { } to illustrate ranges currently in the range set, and < > 176 // to illustrate rangeToAdd. 177 Cut<C> lbToAdd = rangeToAdd.lowerBound; 178 Cut<C> ubToAdd = rangeToAdd.upperBound; 179 180 Entry<Cut<C>, Range<C>> entryBelowLb = rangesByLowerBound.lowerEntry(lbToAdd); 181 if (entryBelowLb != null) { 182 // { < 183 Range<C> rangeBelowLb = entryBelowLb.getValue(); 184 if (rangeBelowLb.upperBound.compareTo(lbToAdd) >= 0) { 185 // { < }, and we will need to coalesce 186 if (rangeBelowLb.upperBound.compareTo(ubToAdd) >= 0) { 187 // { < > } 188 ubToAdd = rangeBelowLb.upperBound; 189 /* 190 * TODO(cpovirk): can we just "return;" here? Or, can we remove this if() entirely? If 191 * not, add tests to demonstrate the problem with each approach 192 */ 193 } 194 lbToAdd = rangeBelowLb.lowerBound; 195 } 196 } 197 198 Entry<Cut<C>, Range<C>> entryBelowUb = rangesByLowerBound.floorEntry(ubToAdd); 199 if (entryBelowUb != null) { 200 // { > 201 Range<C> rangeBelowUb = entryBelowUb.getValue(); 202 if (rangeBelowUb.upperBound.compareTo(ubToAdd) >= 0) { 203 // { > }, and we need to coalesce 204 ubToAdd = rangeBelowUb.upperBound; 205 } 206 } 207 208 // Remove ranges which are strictly enclosed. 209 rangesByLowerBound.subMap(lbToAdd, ubToAdd).clear(); 210 211 replaceRangeWithSameLowerBound(Range.create(lbToAdd, ubToAdd)); 212 } 213 214 @Override 215 public void remove(Range<C> rangeToRemove) { 216 checkNotNull(rangeToRemove); 217 218 if (rangeToRemove.isEmpty()) { 219 return; 220 } 221 222 // We will use { } to illustrate ranges currently in the range set, and < > 223 // to illustrate rangeToRemove. 224 225 Entry<Cut<C>, Range<C>> entryBelowLb = rangesByLowerBound.lowerEntry(rangeToRemove.lowerBound); 226 if (entryBelowLb != null) { 227 // { < 228 Range<C> rangeBelowLb = entryBelowLb.getValue(); 229 if (rangeBelowLb.upperBound.compareTo(rangeToRemove.lowerBound) >= 0) { 230 // { < }, and we will need to subdivide 231 if (rangeToRemove.hasUpperBound() 232 && rangeBelowLb.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { 233 // { < > } 234 replaceRangeWithSameLowerBound( 235 Range.create(rangeToRemove.upperBound, rangeBelowLb.upperBound)); 236 } 237 replaceRangeWithSameLowerBound( 238 Range.create(rangeBelowLb.lowerBound, rangeToRemove.lowerBound)); 239 } 240 } 241 242 Entry<Cut<C>, Range<C>> entryBelowUb = rangesByLowerBound.floorEntry(rangeToRemove.upperBound); 243 if (entryBelowUb != null) { 244 // { > 245 Range<C> rangeBelowUb = entryBelowUb.getValue(); 246 if (rangeToRemove.hasUpperBound() 247 && rangeBelowUb.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { 248 // { > } 249 replaceRangeWithSameLowerBound( 250 Range.create(rangeToRemove.upperBound, rangeBelowUb.upperBound)); 251 } 252 } 253 254 rangesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); 255 } 256 257 private void replaceRangeWithSameLowerBound(Range<C> range) { 258 if (range.isEmpty()) { 259 rangesByLowerBound.remove(range.lowerBound); 260 } else { 261 rangesByLowerBound.put(range.lowerBound, range); 262 } 263 } 264 265 @LazyInit private transient @Nullable RangeSet<C> complement; 266 267 @Override 268 public RangeSet<C> complement() { 269 RangeSet<C> result = complement; 270 if (result == null) { 271 result = complement = new Complement(); 272 } 273 return result; 274 } 275 276 @VisibleForTesting 277 static final class RangesByUpperBound<C extends Comparable<?>> 278 extends AbstractNavigableMap<Cut<C>, Range<C>> { 279 private final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; 280 281 /** 282 * upperBoundWindow represents the headMap/subMap/tailMap view of the entire "ranges by upper 283 * bound" map; it's a constraint on the *keys*, and does not affect the values. 284 */ 285 private final Range<Cut<C>> upperBoundWindow; 286 287 RangesByUpperBound(NavigableMap<Cut<C>, Range<C>> rangesByLowerBound) { 288 this.rangesByLowerBound = rangesByLowerBound; 289 this.upperBoundWindow = Range.all(); 290 } 291 292 private RangesByUpperBound( 293 NavigableMap<Cut<C>, Range<C>> rangesByLowerBound, Range<Cut<C>> upperBoundWindow) { 294 this.rangesByLowerBound = rangesByLowerBound; 295 this.upperBoundWindow = upperBoundWindow; 296 } 297 298 private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> window) { 299 if (window.isConnected(upperBoundWindow)) { 300 return new RangesByUpperBound<>(rangesByLowerBound, window.intersection(upperBoundWindow)); 301 } else { 302 return ImmutableSortedMap.of(); 303 } 304 } 305 306 @Override 307 public NavigableMap<Cut<C>, Range<C>> subMap( 308 Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { 309 return subMap( 310 Range.range( 311 fromKey, BoundType.forBoolean(fromInclusive), 312 toKey, BoundType.forBoolean(toInclusive))); 313 } 314 315 @Override 316 public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { 317 return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); 318 } 319 320 @Override 321 public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { 322 return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); 323 } 324 325 @Override 326 public Comparator<? super Cut<C>> comparator() { 327 return Ordering.natural(); 328 } 329 330 @Override 331 public boolean containsKey(@Nullable Object key) { 332 return get(key) != null; 333 } 334 335 @Override 336 public @Nullable Range<C> get(@Nullable Object key) { 337 if (key instanceof Cut) { 338 try { 339 @SuppressWarnings("unchecked") // we catch CCEs 340 Cut<C> cut = (Cut<C>) key; 341 if (!upperBoundWindow.contains(cut)) { 342 return null; 343 } 344 Entry<Cut<C>, Range<C>> candidate = rangesByLowerBound.lowerEntry(cut); 345 if (candidate != null && candidate.getValue().upperBound.equals(cut)) { 346 return candidate.getValue(); 347 } 348 } catch (ClassCastException e) { 349 return null; 350 } 351 } 352 return null; 353 } 354 355 @Override 356 Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { 357 /* 358 * We want to start the iteration at the first range where the upper bound is in 359 * upperBoundWindow. 360 */ 361 Iterator<Range<C>> backingItr; 362 if (!upperBoundWindow.hasLowerBound()) { 363 backingItr = rangesByLowerBound.values().iterator(); 364 } else { 365 Entry<Cut<C>, Range<C>> lowerEntry = 366 rangesByLowerBound.lowerEntry(upperBoundWindow.lowerEndpoint()); 367 if (lowerEntry == null) { 368 backingItr = rangesByLowerBound.values().iterator(); 369 } else if (upperBoundWindow.lowerBound.isLessThan(lowerEntry.getValue().upperBound)) { 370 backingItr = rangesByLowerBound.tailMap(lowerEntry.getKey(), true).values().iterator(); 371 } else { 372 backingItr = 373 rangesByLowerBound 374 .tailMap(upperBoundWindow.lowerEndpoint(), true) 375 .values() 376 .iterator(); 377 } 378 } 379 return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { 380 @Override 381 protected @Nullable Entry<Cut<C>, Range<C>> computeNext() { 382 if (!backingItr.hasNext()) { 383 return endOfData(); 384 } 385 Range<C> range = backingItr.next(); 386 if (upperBoundWindow.upperBound.isLessThan(range.upperBound)) { 387 return endOfData(); 388 } else { 389 return immutableEntry(range.upperBound, range); 390 } 391 } 392 }; 393 } 394 395 @Override 396 Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { 397 Collection<Range<C>> candidates; 398 if (upperBoundWindow.hasUpperBound()) { 399 candidates = 400 rangesByLowerBound 401 .headMap(upperBoundWindow.upperEndpoint(), false) 402 .descendingMap() 403 .values(); 404 } else { 405 candidates = rangesByLowerBound.descendingMap().values(); 406 } 407 PeekingIterator<Range<C>> backingItr = peekingIterator(candidates.iterator()); 408 if (backingItr.hasNext() 409 && upperBoundWindow.upperBound.isLessThan(backingItr.peek().upperBound)) { 410 backingItr.next(); 411 } 412 return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { 413 @Override 414 protected @Nullable Entry<Cut<C>, Range<C>> computeNext() { 415 if (!backingItr.hasNext()) { 416 return endOfData(); 417 } 418 Range<C> range = backingItr.next(); 419 return upperBoundWindow.lowerBound.isLessThan(range.upperBound) 420 ? immutableEntry(range.upperBound, range) 421 : endOfData(); 422 } 423 }; 424 } 425 426 @Override 427 public int size() { 428 if (upperBoundWindow.equals(Range.all())) { 429 return rangesByLowerBound.size(); 430 } 431 return Iterators.size(entryIterator()); 432 } 433 434 @Override 435 public boolean isEmpty() { 436 return upperBoundWindow.equals(Range.all()) 437 ? rangesByLowerBound.isEmpty() 438 : !entryIterator().hasNext(); 439 } 440 } 441 442 private static final class ComplementRangesByLowerBound<C extends Comparable<?>> 443 extends AbstractNavigableMap<Cut<C>, Range<C>> { 444 private final NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound; 445 private final NavigableMap<Cut<C>, Range<C>> positiveRangesByUpperBound; 446 447 /** 448 * complementLowerBoundWindow represents the headMap/subMap/tailMap view of the entire 449 * "complement ranges by lower bound" map; it's a constraint on the *keys*, and does not affect 450 * the values. 451 */ 452 private final Range<Cut<C>> complementLowerBoundWindow; 453 454 ComplementRangesByLowerBound(NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound) { 455 this(positiveRangesByLowerBound, Range.all()); 456 } 457 458 private ComplementRangesByLowerBound( 459 NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound, Range<Cut<C>> window) { 460 this.positiveRangesByLowerBound = positiveRangesByLowerBound; 461 this.positiveRangesByUpperBound = new RangesByUpperBound<>(positiveRangesByLowerBound); 462 this.complementLowerBoundWindow = window; 463 } 464 465 private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> subWindow) { 466 if (!complementLowerBoundWindow.isConnected(subWindow)) { 467 return ImmutableSortedMap.of(); 468 } else { 469 subWindow = subWindow.intersection(complementLowerBoundWindow); 470 return new ComplementRangesByLowerBound<>(positiveRangesByLowerBound, subWindow); 471 } 472 } 473 474 @Override 475 public NavigableMap<Cut<C>, Range<C>> subMap( 476 Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { 477 return subMap( 478 Range.range( 479 fromKey, BoundType.forBoolean(fromInclusive), 480 toKey, BoundType.forBoolean(toInclusive))); 481 } 482 483 @Override 484 public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { 485 return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); 486 } 487 488 @Override 489 public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { 490 return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); 491 } 492 493 @Override 494 public Comparator<? super Cut<C>> comparator() { 495 return Ordering.natural(); 496 } 497 498 @Override 499 Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { 500 /* 501 * firstComplementRangeLowerBound is the first complement range lower bound inside 502 * complementLowerBoundWindow. Complement range lower bounds are either positive range upper 503 * bounds, or Cut.belowAll(). 504 * 505 * positiveItr starts at the first positive range with lower bound greater than 506 * firstComplementRangeLowerBound. (Positive range lower bounds correspond to complement range 507 * upper bounds.) 508 */ 509 Collection<Range<C>> positiveRanges; 510 if (complementLowerBoundWindow.hasLowerBound()) { 511 positiveRanges = 512 positiveRangesByUpperBound 513 .tailMap( 514 complementLowerBoundWindow.lowerEndpoint(), 515 complementLowerBoundWindow.lowerBoundType() == BoundType.CLOSED) 516 .values(); 517 } else { 518 positiveRanges = positiveRangesByUpperBound.values(); 519 } 520 PeekingIterator<Range<C>> positiveItr = peekingIterator(positiveRanges.iterator()); 521 Cut<C> firstComplementRangeLowerBound; 522 if (complementLowerBoundWindow.contains(Cut.belowAll()) 523 && (!positiveItr.hasNext() || positiveItr.peek().lowerBound != Cut.<C>belowAll())) { 524 firstComplementRangeLowerBound = Cut.belowAll(); 525 } else if (positiveItr.hasNext()) { 526 firstComplementRangeLowerBound = positiveItr.next().upperBound; 527 } else { 528 return emptyIterator(); 529 } 530 return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { 531 Cut<C> nextComplementRangeLowerBound = firstComplementRangeLowerBound; 532 533 @Override 534 protected @Nullable Entry<Cut<C>, Range<C>> computeNext() { 535 if (complementLowerBoundWindow.upperBound.isLessThan(nextComplementRangeLowerBound) 536 || nextComplementRangeLowerBound == Cut.<C>aboveAll()) { 537 return endOfData(); 538 } 539 Range<C> negativeRange; 540 if (positiveItr.hasNext()) { 541 Range<C> positiveRange = positiveItr.next(); 542 negativeRange = Range.create(nextComplementRangeLowerBound, positiveRange.lowerBound); 543 nextComplementRangeLowerBound = positiveRange.upperBound; 544 } else { 545 negativeRange = Range.create(nextComplementRangeLowerBound, Cut.aboveAll()); 546 nextComplementRangeLowerBound = Cut.aboveAll(); 547 } 548 return immutableEntry(negativeRange.lowerBound, negativeRange); 549 } 550 }; 551 } 552 553 @Override 554 Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { 555 /* 556 * firstComplementRangeUpperBound is the upper bound of the last complement range with lower 557 * bound inside complementLowerBoundWindow. 558 * 559 * positiveItr starts at the first positive range with upper bound less than 560 * firstComplementRangeUpperBound. (Positive range upper bounds correspond to complement range 561 * lower bounds.) 562 */ 563 Cut<C> startingPoint = 564 complementLowerBoundWindow.hasUpperBound() 565 ? complementLowerBoundWindow.upperEndpoint() 566 : Cut.aboveAll(); 567 boolean inclusive = 568 complementLowerBoundWindow.hasUpperBound() 569 && complementLowerBoundWindow.upperBoundType() == BoundType.CLOSED; 570 PeekingIterator<Range<C>> positiveItr = 571 peekingIterator( 572 positiveRangesByUpperBound 573 .headMap(startingPoint, inclusive) 574 .descendingMap() 575 .values() 576 .iterator()); 577 Cut<C> cut; 578 if (positiveItr.hasNext()) { 579 cut = 580 (positiveItr.peek().upperBound == Cut.<C>aboveAll()) 581 ? positiveItr.next().lowerBound 582 : positiveRangesByLowerBound.higherKey(positiveItr.peek().upperBound); 583 } else if (!complementLowerBoundWindow.contains(Cut.belowAll()) 584 || positiveRangesByLowerBound.containsKey(Cut.belowAll())) { 585 return emptyIterator(); 586 } else { 587 cut = positiveRangesByLowerBound.higherKey(Cut.belowAll()); 588 } 589 Cut<C> firstComplementRangeUpperBound = firstNonNull(cut, Cut.aboveAll()); 590 return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { 591 Cut<C> nextComplementRangeUpperBound = firstComplementRangeUpperBound; 592 593 @Override 594 protected @Nullable Entry<Cut<C>, Range<C>> computeNext() { 595 if (nextComplementRangeUpperBound == Cut.<C>belowAll()) { 596 return endOfData(); 597 } else if (positiveItr.hasNext()) { 598 Range<C> positiveRange = positiveItr.next(); 599 Range<C> negativeRange = 600 Range.create(positiveRange.upperBound, nextComplementRangeUpperBound); 601 nextComplementRangeUpperBound = positiveRange.lowerBound; 602 if (complementLowerBoundWindow.lowerBound.isLessThan(negativeRange.lowerBound)) { 603 return immutableEntry(negativeRange.lowerBound, negativeRange); 604 } 605 } else if (complementLowerBoundWindow.lowerBound.isLessThan(Cut.belowAll())) { 606 Range<C> negativeRange = Range.create(Cut.belowAll(), nextComplementRangeUpperBound); 607 nextComplementRangeUpperBound = Cut.belowAll(); 608 return immutableEntry(Cut.belowAll(), negativeRange); 609 } 610 return endOfData(); 611 } 612 }; 613 } 614 615 @Override 616 public int size() { 617 return Iterators.size(entryIterator()); 618 } 619 620 @Override 621 public @Nullable Range<C> get(@Nullable Object key) { 622 if (key instanceof Cut) { 623 try { 624 @SuppressWarnings("unchecked") 625 Cut<C> cut = (Cut<C>) key; 626 // tailMap respects the current window 627 Entry<Cut<C>, Range<C>> firstEntry = tailMap(cut, true).firstEntry(); 628 if (firstEntry != null && firstEntry.getKey().equals(cut)) { 629 return firstEntry.getValue(); 630 } 631 } catch (ClassCastException e) { 632 return null; 633 } 634 } 635 return null; 636 } 637 638 @Override 639 public boolean containsKey(@Nullable Object key) { 640 return get(key) != null; 641 } 642 } 643 644 private final class Complement extends TreeRangeSet<C> { 645 Complement() { 646 super(new ComplementRangesByLowerBound<C>(TreeRangeSet.this.rangesByLowerBound)); 647 } 648 649 @Override 650 public void add(Range<C> rangeToAdd) { 651 TreeRangeSet.this.remove(rangeToAdd); 652 } 653 654 @Override 655 public void remove(Range<C> rangeToRemove) { 656 TreeRangeSet.this.add(rangeToRemove); 657 } 658 659 @Override 660 public boolean contains(C value) { 661 return !TreeRangeSet.this.contains(value); 662 } 663 664 @Override 665 public RangeSet<C> complement() { 666 return TreeRangeSet.this; 667 } 668 } 669 670 private static final class SubRangeSetRangesByLowerBound<C extends Comparable<?>> 671 extends AbstractNavigableMap<Cut<C>, Range<C>> { 672 /** 673 * lowerBoundWindow is the headMap/subMap/tailMap view; it only restricts the keys, and does not 674 * affect the values. 675 */ 676 private final Range<Cut<C>> lowerBoundWindow; 677 678 /** 679 * restriction is the subRangeSet view; ranges are truncated to their intersection with 680 * restriction. 681 */ 682 private final Range<C> restriction; 683 684 private final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; 685 private final NavigableMap<Cut<C>, Range<C>> rangesByUpperBound; 686 687 private SubRangeSetRangesByLowerBound( 688 Range<Cut<C>> lowerBoundWindow, 689 Range<C> restriction, 690 NavigableMap<Cut<C>, Range<C>> rangesByLowerBound) { 691 this.lowerBoundWindow = checkNotNull(lowerBoundWindow); 692 this.restriction = checkNotNull(restriction); 693 this.rangesByLowerBound = checkNotNull(rangesByLowerBound); 694 this.rangesByUpperBound = new RangesByUpperBound<>(rangesByLowerBound); 695 } 696 697 private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> window) { 698 if (!window.isConnected(lowerBoundWindow)) { 699 return ImmutableSortedMap.of(); 700 } else { 701 return new SubRangeSetRangesByLowerBound<>( 702 lowerBoundWindow.intersection(window), restriction, rangesByLowerBound); 703 } 704 } 705 706 @Override 707 public NavigableMap<Cut<C>, Range<C>> subMap( 708 Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { 709 return subMap( 710 Range.range( 711 fromKey, 712 BoundType.forBoolean(fromInclusive), 713 toKey, 714 BoundType.forBoolean(toInclusive))); 715 } 716 717 @Override 718 public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { 719 return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); 720 } 721 722 @Override 723 public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { 724 return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); 725 } 726 727 @Override 728 public Comparator<? super Cut<C>> comparator() { 729 return Ordering.natural(); 730 } 731 732 @Override 733 public boolean containsKey(@Nullable Object key) { 734 return get(key) != null; 735 } 736 737 @Override 738 public @Nullable Range<C> get(@Nullable Object key) { 739 if (key instanceof Cut) { 740 try { 741 @SuppressWarnings("unchecked") // we catch CCE's 742 Cut<C> cut = (Cut<C>) key; 743 if (!lowerBoundWindow.contains(cut) 744 || cut.compareTo(restriction.lowerBound) < 0 745 || cut.compareTo(restriction.upperBound) >= 0) { 746 return null; 747 } else if (cut.equals(restriction.lowerBound)) { 748 // it might be present, truncated on the left 749 Range<C> candidate = valueOrNull(rangesByLowerBound.floorEntry(cut)); 750 if (candidate != null && candidate.upperBound.compareTo(restriction.lowerBound) > 0) { 751 return candidate.intersection(restriction); 752 } 753 } else { 754 Range<C> result = rangesByLowerBound.get(cut); 755 if (result != null) { 756 return result.intersection(restriction); 757 } 758 } 759 } catch (ClassCastException e) { 760 return null; 761 } 762 } 763 return null; 764 } 765 766 @Override 767 Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { 768 if (restriction.isEmpty()) { 769 return emptyIterator(); 770 } 771 Iterator<Range<C>> completeRangeItr; 772 if (lowerBoundWindow.upperBound.isLessThan(restriction.lowerBound)) { 773 return emptyIterator(); 774 } else if (lowerBoundWindow.lowerBound.isLessThan(restriction.lowerBound)) { 775 // starts at the first range with upper bound strictly greater than restriction.lowerBound 776 completeRangeItr = 777 rangesByUpperBound.tailMap(restriction.lowerBound, false).values().iterator(); 778 } else { 779 // starts at the first range with lower bound above lowerBoundWindow.lowerBound 780 completeRangeItr = 781 rangesByLowerBound 782 .tailMap( 783 lowerBoundWindow.lowerBound.endpoint(), 784 lowerBoundWindow.lowerBoundType() == BoundType.CLOSED) 785 .values() 786 .iterator(); 787 } 788 Cut<Cut<C>> upperBoundOnLowerBounds = 789 Ordering.<Cut<Cut<C>>>natural() 790 .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); 791 return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { 792 @Override 793 protected @Nullable Entry<Cut<C>, Range<C>> computeNext() { 794 if (!completeRangeItr.hasNext()) { 795 return endOfData(); 796 } 797 Range<C> nextRange = completeRangeItr.next(); 798 if (upperBoundOnLowerBounds.isLessThan(nextRange.lowerBound)) { 799 return endOfData(); 800 } else { 801 nextRange = nextRange.intersection(restriction); 802 return immutableEntry(nextRange.lowerBound, nextRange); 803 } 804 } 805 }; 806 } 807 808 @Override 809 Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { 810 if (restriction.isEmpty()) { 811 return emptyIterator(); 812 } 813 Cut<Cut<C>> upperBoundOnLowerBounds = 814 Ordering.<Cut<Cut<C>>>natural() 815 .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); 816 Iterator<Range<C>> completeRangeItr = 817 rangesByLowerBound 818 .headMap( 819 upperBoundOnLowerBounds.endpoint(), 820 upperBoundOnLowerBounds.typeAsUpperBound() == BoundType.CLOSED) 821 .descendingMap() 822 .values() 823 .iterator(); 824 return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { 825 @Override 826 protected @Nullable Entry<Cut<C>, Range<C>> computeNext() { 827 if (!completeRangeItr.hasNext()) { 828 return endOfData(); 829 } 830 Range<C> nextRange = completeRangeItr.next(); 831 if (restriction.lowerBound.compareTo(nextRange.upperBound) >= 0) { 832 return endOfData(); 833 } 834 nextRange = nextRange.intersection(restriction); 835 if (lowerBoundWindow.contains(nextRange.lowerBound)) { 836 return immutableEntry(nextRange.lowerBound, nextRange); 837 } else { 838 return endOfData(); 839 } 840 } 841 }; 842 } 843 844 @Override 845 public int size() { 846 return Iterators.size(entryIterator()); 847 } 848 } 849 850 @Override 851 public RangeSet<C> subRangeSet(Range<C> view) { 852 return view.equals(Range.all()) ? this : new SubRangeSet(view); 853 } 854 855 private final class SubRangeSet extends TreeRangeSet<C> { 856 private final Range<C> restriction; 857 858 SubRangeSet(Range<C> restriction) { 859 super( 860 new SubRangeSetRangesByLowerBound<C>( 861 Range.all(), restriction, TreeRangeSet.this.rangesByLowerBound)); 862 this.restriction = restriction; 863 } 864 865 @Override 866 public boolean encloses(Range<C> range) { 867 if (!restriction.isEmpty() && restriction.encloses(range)) { 868 Range<C> enclosing = TreeRangeSet.this.rangeEnclosing(range); 869 return enclosing != null && !enclosing.intersection(restriction).isEmpty(); 870 } 871 return false; 872 } 873 874 @Override 875 public @Nullable Range<C> rangeContaining(C value) { 876 if (!restriction.contains(value)) { 877 return null; 878 } 879 Range<C> result = TreeRangeSet.this.rangeContaining(value); 880 return (result == null) ? null : result.intersection(restriction); 881 } 882 883 @Override 884 public void add(Range<C> rangeToAdd) { 885 checkArgument( 886 restriction.encloses(rangeToAdd), 887 "Cannot add range %s to subRangeSet(%s)", 888 rangeToAdd, 889 restriction); 890 TreeRangeSet.this.add(rangeToAdd); 891 } 892 893 @Override 894 public void remove(Range<C> rangeToRemove) { 895 if (rangeToRemove.isConnected(restriction)) { 896 TreeRangeSet.this.remove(rangeToRemove.intersection(restriction)); 897 } 898 } 899 900 @Override 901 public boolean contains(C value) { 902 return restriction.contains(value) && TreeRangeSet.this.contains(value); 903 } 904 905 @Override 906 public void clear() { 907 TreeRangeSet.this.remove(restriction); 908 } 909 910 @Override 911 public RangeSet<C> subRangeSet(Range<C> view) { 912 if (view.encloses(restriction)) { 913 return this; 914 } else if (view.isConnected(restriction)) { 915 return new SubRangeSet(restriction.intersection(view)); 916 } else { 917 return ImmutableRangeSet.of(); 918 } 919 } 920 } 921 922 private static final long serialVersionUID = 25682166307111266L; 923}