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