/* * Copyright (C) 2011 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeMap; import javax.annotation.Nullable; /** * An implementation of {@link RangeSet} backed by a {@link TreeMap}. * * @author Louis Wasserman * @since 14.0 */ @Beta @GwtIncompatible("uses NavigableMap") public class TreeRangeSet<C extends Comparable<?>> extends AbstractRangeSet<C> { @VisibleForTesting final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; /** * Creates an empty {@code TreeRangeSet} instance. */ public static <C extends Comparable<?>> TreeRangeSet<C> create() { return new TreeRangeSet<C>(new TreeMap<Cut<C>, Range<C>>()); } /** * Returns a {@code TreeRangeSet} initialized with the ranges in the specified range set. */ public static <C extends Comparable<?>> TreeRangeSet<C> create(RangeSet<C> rangeSet) { TreeRangeSet<C> result = create(); result.addAll(rangeSet); return result; } private TreeRangeSet(NavigableMap<Cut<C>, Range<C>> rangesByLowerCut) { this.rangesByLowerBound = rangesByLowerCut; } private transient Set<Range<C>> asRanges; @Override public Set<Range<C>> asRanges() { Set<Range<C>> result = asRanges; return (result == null) ? asRanges = new AsRanges() : result; } final class AsRanges extends ForwardingCollection<Range<C>> implements Set<Range<C>> { @Override protected Collection<Range<C>> delegate() { return rangesByLowerBound.values(); } @Override public int hashCode() { return Sets.hashCodeImpl(this); } @Override public boolean equals(@Nullable Object o) { return Sets.equalsImpl(this, o); } } @Override @Nullable public Range<C> rangeContaining(C value) { checkNotNull(value); Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(Cut.belowValue(value)); if (floorEntry != null && floorEntry.getValue().contains(value)) { return floorEntry.getValue(); } else { // TODO(kevinb): revisit this design choice return null; } } @Override public boolean encloses(Range<C> range) { checkNotNull(range); Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); return floorEntry != null && floorEntry.getValue().encloses(range); } @Nullable private Range<C> rangeEnclosing(Range<C> range) { checkNotNull(range); Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); return (floorEntry != null && floorEntry.getValue().encloses(range)) ? floorEntry.getValue() : null; } @Override public Range<C> span() { Entry<Cut<C>, Range<C>> firstEntry = rangesByLowerBound.firstEntry(); Entry<Cut<C>, Range<C>> lastEntry = rangesByLowerBound.lastEntry(); if (firstEntry == null) { throw new NoSuchElementException(); } return Range.create(firstEntry.getValue().lowerBound, lastEntry.getValue().upperBound); } @Override public void add(Range<C> rangeToAdd) { checkNotNull(rangeToAdd); if (rangeToAdd.isEmpty()) { return; } // We will use { } to illustrate ranges currently in the range set, and < > // to illustrate rangeToAdd. Cut<C> lbToAdd = rangeToAdd.lowerBound; Cut<C> ubToAdd = rangeToAdd.upperBound; Entry<Cut<C>, Range<C>> entryBelowLB = rangesByLowerBound.lowerEntry(lbToAdd); if (entryBelowLB != null) { // { < Range<C> rangeBelowLB = entryBelowLB.getValue(); if (rangeBelowLB.upperBound.compareTo(lbToAdd) >= 0) { // { < }, and we will need to coalesce if (rangeBelowLB.upperBound.compareTo(ubToAdd) >= 0) { // { < > } ubToAdd = rangeBelowLB.upperBound; /* * TODO(cpovirk): can we just "return;" here? Or, can we remove this if() entirely? If * not, add tests to demonstrate the problem with each approach */ } lbToAdd = rangeBelowLB.lowerBound; } } Entry<Cut<C>, Range<C>> entryBelowUB = rangesByLowerBound.floorEntry(ubToAdd); if (entryBelowUB != null) { // { > Range<C> rangeBelowUB = entryBelowUB.getValue(); if (rangeBelowUB.upperBound.compareTo(ubToAdd) >= 0) { // { > }, and we need to coalesce ubToAdd = rangeBelowUB.upperBound; } } // Remove ranges which are strictly enclosed. rangesByLowerBound.subMap(lbToAdd, ubToAdd).clear(); replaceRangeWithSameLowerBound(Range.create(lbToAdd, ubToAdd)); } @Override public void remove(Range<C> rangeToRemove) { checkNotNull(rangeToRemove); if (rangeToRemove.isEmpty()) { return; } // We will use { } to illustrate ranges currently in the range set, and < > // to illustrate rangeToRemove. Entry<Cut<C>, Range<C>> entryBelowLB = rangesByLowerBound.lowerEntry(rangeToRemove.lowerBound); if (entryBelowLB != null) { // { < Range<C> rangeBelowLB = entryBelowLB.getValue(); if (rangeBelowLB.upperBound.compareTo(rangeToRemove.lowerBound) >= 0) { // { < }, and we will need to subdivide if (rangeToRemove.hasUpperBound() && rangeBelowLB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { // { < > } replaceRangeWithSameLowerBound( Range.create(rangeToRemove.upperBound, rangeBelowLB.upperBound)); } replaceRangeWithSameLowerBound( Range.create(rangeBelowLB.lowerBound, rangeToRemove.lowerBound)); } } Entry<Cut<C>, Range<C>> entryBelowUB = rangesByLowerBound.floorEntry(rangeToRemove.upperBound); if (entryBelowUB != null) { // { > Range<C> rangeBelowUB = entryBelowUB.getValue(); if (rangeToRemove.hasUpperBound() && rangeBelowUB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { // { > } replaceRangeWithSameLowerBound( Range.create(rangeToRemove.upperBound, rangeBelowUB.upperBound)); } } rangesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); } private void replaceRangeWithSameLowerBound(Range<C> range) { if (range.isEmpty()) { rangesByLowerBound.remove(range.lowerBound); } else { rangesByLowerBound.put(range.lowerBound, range); } } private transient RangeSet<C> complement; @Override public RangeSet<C> complement() { RangeSet<C> result = complement; return (result == null) ? complement = new Complement() : result; } @VisibleForTesting static final class RangesByUpperBound<C extends Comparable<?>> extends AbstractNavigableMap<Cut<C>, Range<C>> { private final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; /** * upperBoundWindow represents the headMap/subMap/tailMap view of the entire "ranges by upper * bound" map; it's a constraint on the *keys*, and does not affect the values. */ private final Range<Cut<C>> upperBoundWindow; RangesByUpperBound(NavigableMap<Cut<C>, Range<C>> rangesByLowerBound) { this.rangesByLowerBound = rangesByLowerBound; this.upperBoundWindow = Range.all(); } private RangesByUpperBound( NavigableMap<Cut<C>, Range<C>> rangesByLowerBound, Range<Cut<C>> upperBoundWindow) { this.rangesByLowerBound = rangesByLowerBound; this.upperBoundWindow = upperBoundWindow; } private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> window) { if (window.isConnected(upperBoundWindow)) { return new RangesByUpperBound<C>(rangesByLowerBound, window.intersection(upperBoundWindow)); } else { return ImmutableSortedMap.of(); } } @Override public NavigableMap<Cut<C>, Range<C>> subMap( Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { return subMap(Range.range( fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); } @Override public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); } @Override public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); } @Override public Comparator<? super Cut<C>> comparator() { return Ordering.<Cut<C>>natural(); } @Override public boolean containsKey(@Nullable Object key) { return get(key) != null; } @Override public Range<C> get(@Nullable Object key) { if (key instanceof Cut) { try { @SuppressWarnings("unchecked") // we catch CCEs Cut<C> cut = (Cut<C>) key; if (!upperBoundWindow.contains(cut)) { return null; } Entry<Cut<C>, Range<C>> candidate = rangesByLowerBound.lowerEntry(cut); if (candidate != null && candidate.getValue().upperBound.equals(cut)) { return candidate.getValue(); } } catch (ClassCastException e) { return null; } } return null; } @Override Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { /* * We want to start the iteration at the first range where the upper bound is in * upperBoundWindow. */ final Iterator<Range<C>> backingItr; if (!upperBoundWindow.hasLowerBound()) { backingItr = rangesByLowerBound.values().iterator(); } else { Entry<Cut<C>, Range<C>> lowerEntry = rangesByLowerBound.lowerEntry(upperBoundWindow.lowerEndpoint()); if (lowerEntry == null) { backingItr = rangesByLowerBound.values().iterator(); } else if (upperBoundWindow.lowerBound.isLessThan(lowerEntry.getValue().upperBound)) { backingItr = rangesByLowerBound.tailMap(lowerEntry.getKey(), true).values().iterator(); } else { backingItr = rangesByLowerBound.tailMap(upperBoundWindow.lowerEndpoint(), true) .values().iterator(); } } return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { @Override protected Entry<Cut<C>, Range<C>> computeNext() { if (!backingItr.hasNext()) { return endOfData(); } Range<C> range = backingItr.next(); if (upperBoundWindow.upperBound.isLessThan(range.upperBound)) { return endOfData(); } else { return Maps.immutableEntry(range.upperBound, range); } } }; } @Override Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { Collection<Range<C>> candidates; if (upperBoundWindow.hasUpperBound()) { candidates = rangesByLowerBound.headMap(upperBoundWindow.upperEndpoint(), false) .descendingMap().values(); } else { candidates = rangesByLowerBound.descendingMap().values(); } final PeekingIterator<Range<C>> backingItr = Iterators.peekingIterator(candidates.iterator()); if (backingItr.hasNext() && upperBoundWindow.upperBound.isLessThan(backingItr.peek().upperBound)) { backingItr.next(); } return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { @Override protected Entry<Cut<C>, Range<C>> computeNext() { if (!backingItr.hasNext()) { return endOfData(); } Range<C> range = backingItr.next(); return upperBoundWindow.lowerBound.isLessThan(range.upperBound) ? Maps.immutableEntry(range.upperBound, range) : endOfData(); } }; } @Override public int size() { if (upperBoundWindow.equals(Range.all())) { return rangesByLowerBound.size(); } return Iterators.size(entryIterator()); } @Override public boolean isEmpty() { return upperBoundWindow.equals(Range.all()) ? rangesByLowerBound.isEmpty() : !entryIterator().hasNext(); } } private static final class ComplementRangesByLowerBound<C extends Comparable<?>> extends AbstractNavigableMap<Cut<C>, Range<C>> { private final NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound; private final NavigableMap<Cut<C>, Range<C>> positiveRangesByUpperBound; /** * complementLowerBoundWindow represents the headMap/subMap/tailMap view of the entire * "complement ranges by lower bound" map; it's a constraint on the *keys*, and does not affect * the values. */ private final Range<Cut<C>> complementLowerBoundWindow; ComplementRangesByLowerBound(NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound) { this(positiveRangesByLowerBound, Range.<Cut<C>>all()); } private ComplementRangesByLowerBound(NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound, Range<Cut<C>> window) { this.positiveRangesByLowerBound = positiveRangesByLowerBound; this.positiveRangesByUpperBound = new RangesByUpperBound<C>(positiveRangesByLowerBound); this.complementLowerBoundWindow = window; } private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> subWindow) { if (!complementLowerBoundWindow.isConnected(subWindow)) { return ImmutableSortedMap.of(); } else { subWindow = subWindow.intersection(complementLowerBoundWindow); return new ComplementRangesByLowerBound<C>(positiveRangesByLowerBound, subWindow); } } @Override public NavigableMap<Cut<C>, Range<C>> subMap( Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { return subMap(Range.range( fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); } @Override public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); } @Override public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); } @Override public Comparator<? super Cut<C>> comparator() { return Ordering.<Cut<C>>natural(); } @Override Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { /* * firstComplementRangeLowerBound is the first complement range lower bound inside * complementLowerBoundWindow. Complement range lower bounds are either positive range upper * bounds, or Cut.belowAll(). * * positiveItr starts at the first positive range with lower bound greater than * firstComplementRangeLowerBound. (Positive range lower bounds correspond to complement range * upper bounds.) */ Collection<Range<C>> positiveRanges; if (complementLowerBoundWindow.hasLowerBound()) { positiveRanges = positiveRangesByUpperBound.tailMap( complementLowerBoundWindow.lowerEndpoint(), complementLowerBoundWindow.lowerBoundType() == BoundType.CLOSED).values(); } else { positiveRanges = positiveRangesByUpperBound.values(); } final PeekingIterator<Range<C>> positiveItr = Iterators.peekingIterator( positiveRanges.iterator()); final Cut<C> firstComplementRangeLowerBound; if (complementLowerBoundWindow.contains(Cut.<C>belowAll()) && (!positiveItr.hasNext() || positiveItr.peek().lowerBound != Cut.<C>belowAll())) { firstComplementRangeLowerBound = Cut.belowAll(); } else if (positiveItr.hasNext()) { firstComplementRangeLowerBound = positiveItr.next().upperBound; } else { return Iterators.emptyIterator(); } return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { Cut<C> nextComplementRangeLowerBound = firstComplementRangeLowerBound; @Override protected Entry<Cut<C>, Range<C>> computeNext() { if (complementLowerBoundWindow.upperBound.isLessThan(nextComplementRangeLowerBound) || nextComplementRangeLowerBound == Cut.<C>aboveAll()) { return endOfData(); } Range<C> negativeRange; if (positiveItr.hasNext()) { Range<C> positiveRange = positiveItr.next(); negativeRange = Range.create(nextComplementRangeLowerBound, positiveRange.lowerBound); nextComplementRangeLowerBound = positiveRange.upperBound; } else { negativeRange = Range.create(nextComplementRangeLowerBound, Cut.<C>aboveAll()); nextComplementRangeLowerBound = Cut.aboveAll(); } return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); } }; } @Override Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { /* * firstComplementRangeUpperBound is the upper bound of the last complement range with lower * bound inside complementLowerBoundWindow. * * positiveItr starts at the first positive range with upper bound less than * firstComplementRangeUpperBound. (Positive range upper bounds correspond to complement range * lower bounds.) */ Cut<C> startingPoint = complementLowerBoundWindow.hasUpperBound() ? complementLowerBoundWindow.upperEndpoint() : Cut.<C>aboveAll(); boolean inclusive = complementLowerBoundWindow.hasUpperBound() && complementLowerBoundWindow.upperBoundType() == BoundType.CLOSED; final PeekingIterator<Range<C>> positiveItr = Iterators.peekingIterator(positiveRangesByUpperBound.headMap(startingPoint, inclusive) .descendingMap().values().iterator()); Cut<C> cut; if (positiveItr.hasNext()) { cut = (positiveItr.peek().upperBound == Cut.<C>aboveAll()) ? positiveItr.next().lowerBound : positiveRangesByLowerBound.higherKey(positiveItr.peek().upperBound); } else if (!complementLowerBoundWindow.contains(Cut.<C>belowAll()) || positiveRangesByLowerBound.containsKey(Cut.belowAll())) { return Iterators.emptyIterator(); } else { cut = positiveRangesByLowerBound.higherKey(Cut.<C>belowAll()); } final Cut<C> firstComplementRangeUpperBound = MoreObjects.firstNonNull(cut, Cut.<C>aboveAll()); return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { Cut<C> nextComplementRangeUpperBound = firstComplementRangeUpperBound; @Override protected Entry<Cut<C>, Range<C>> computeNext() { if (nextComplementRangeUpperBound == Cut.<C>belowAll()) { return endOfData(); } else if (positiveItr.hasNext()) { Range<C> positiveRange = positiveItr.next(); Range<C> negativeRange = Range.create(positiveRange.upperBound, nextComplementRangeUpperBound); nextComplementRangeUpperBound = positiveRange.lowerBound; if (complementLowerBoundWindow.lowerBound.isLessThan(negativeRange.lowerBound)) { return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); } } else if (complementLowerBoundWindow.lowerBound.isLessThan(Cut.<C>belowAll())) { Range<C> negativeRange = Range.create(Cut.<C>belowAll(), nextComplementRangeUpperBound); nextComplementRangeUpperBound = Cut.belowAll(); return Maps.immutableEntry(Cut.<C>belowAll(), negativeRange); } return endOfData(); } }; } @Override public int size() { return Iterators.size(entryIterator()); } @Override @Nullable public Range<C> get(Object key) { if (key instanceof Cut) { try { @SuppressWarnings("unchecked") Cut<C> cut = (Cut<C>) key; // tailMap respects the current window Entry<Cut<C>, Range<C>> firstEntry = tailMap(cut, true).firstEntry(); if (firstEntry != null && firstEntry.getKey().equals(cut)) { return firstEntry.getValue(); } } catch (ClassCastException e) { return null; } } return null; } @Override public boolean containsKey(Object key) { return get(key) != null; } } private final class Complement extends TreeRangeSet<C> { Complement() { super(new ComplementRangesByLowerBound<C>(TreeRangeSet.this.rangesByLowerBound)); } @Override public void add(Range<C> rangeToAdd) { TreeRangeSet.this.remove(rangeToAdd); } @Override public void remove(Range<C> rangeToRemove) { TreeRangeSet.this.add(rangeToRemove); } @Override public boolean contains(C value) { return !TreeRangeSet.this.contains(value); } @Override public RangeSet<C> complement() { return TreeRangeSet.this; } } private static final class SubRangeSetRangesByLowerBound<C extends Comparable<?>> extends AbstractNavigableMap<Cut<C>, Range<C>> { /** * lowerBoundWindow is the headMap/subMap/tailMap view; it only restricts the keys, and does not * affect the values. */ private final Range<Cut<C>> lowerBoundWindow; /** * restriction is the subRangeSet view; ranges are truncated to their intersection with * restriction. */ private final Range<C> restriction; private final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; private final NavigableMap<Cut<C>, Range<C>> rangesByUpperBound; private SubRangeSetRangesByLowerBound(Range<Cut<C>> lowerBoundWindow, Range<C> restriction, NavigableMap<Cut<C>, Range<C>> rangesByLowerBound) { this.lowerBoundWindow = checkNotNull(lowerBoundWindow); this.restriction = checkNotNull(restriction); this.rangesByLowerBound = checkNotNull(rangesByLowerBound); this.rangesByUpperBound = new RangesByUpperBound<C>(rangesByLowerBound); } private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> window) { if (!window.isConnected(lowerBoundWindow)) { return ImmutableSortedMap.of(); } else { return new SubRangeSetRangesByLowerBound<C>( lowerBoundWindow.intersection(window), restriction, rangesByLowerBound); } } @Override public NavigableMap<Cut<C>, Range<C>> subMap( Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { return subMap(Range.range( fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); } @Override public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); } @Override public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); } @Override public Comparator<? super Cut<C>> comparator() { return Ordering.<Cut<C>>natural(); } @Override public boolean containsKey(@Nullable Object key) { return get(key) != null; } @Override @Nullable public Range<C> get(@Nullable Object key) { if (key instanceof Cut) { try { @SuppressWarnings("unchecked") // we catch CCE's Cut<C> cut = (Cut<C>) key; if (!lowerBoundWindow.contains(cut) || cut.compareTo(restriction.lowerBound) < 0 || cut.compareTo(restriction.upperBound) >= 0) { return null; } else if (cut.equals(restriction.lowerBound)) { // it might be present, truncated on the left Range<C> candidate = Maps.valueOrNull(rangesByLowerBound.floorEntry(cut)); if (candidate != null && candidate.upperBound.compareTo(restriction.lowerBound) > 0) { return candidate.intersection(restriction); } } else { Range<C> result = rangesByLowerBound.get(cut); if (result != null) { return result.intersection(restriction); } } } catch (ClassCastException e) { return null; } } return null; } @Override Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { if (restriction.isEmpty()) { return Iterators.emptyIterator(); } final Iterator<Range<C>> completeRangeItr; if (lowerBoundWindow.upperBound.isLessThan(restriction.lowerBound)) { return Iterators.emptyIterator(); } else if (lowerBoundWindow.lowerBound.isLessThan(restriction.lowerBound)) { // starts at the first range with upper bound strictly greater than restriction.lowerBound completeRangeItr = rangesByUpperBound.tailMap(restriction.lowerBound, false).values().iterator(); } else { // starts at the first range with lower bound above lowerBoundWindow.lowerBound completeRangeItr = rangesByLowerBound.tailMap(lowerBoundWindow.lowerBound.endpoint(), lowerBoundWindow.lowerBoundType() == BoundType.CLOSED).values().iterator(); } final Cut<Cut<C>> upperBoundOnLowerBounds = Ordering.natural() .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { @Override protected Entry<Cut<C>, Range<C>> computeNext() { if (!completeRangeItr.hasNext()) { return endOfData(); } Range<C> nextRange = completeRangeItr.next(); if (upperBoundOnLowerBounds.isLessThan(nextRange.lowerBound)) { return endOfData(); } else { nextRange = nextRange.intersection(restriction); return Maps.immutableEntry(nextRange.lowerBound, nextRange); } } }; } @Override Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { if (restriction.isEmpty()) { return Iterators.emptyIterator(); } Cut<Cut<C>> upperBoundOnLowerBounds = Ordering.natural() .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); final Iterator<Range<C>> completeRangeItr = rangesByLowerBound.headMap( upperBoundOnLowerBounds.endpoint(), upperBoundOnLowerBounds.typeAsUpperBound() == BoundType.CLOSED) .descendingMap().values().iterator(); return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { @Override protected Entry<Cut<C>, Range<C>> computeNext() { if (!completeRangeItr.hasNext()) { return endOfData(); } Range<C> nextRange = completeRangeItr.next(); if (restriction.lowerBound.compareTo(nextRange.upperBound) >= 0) { return endOfData(); } nextRange = nextRange.intersection(restriction); if (lowerBoundWindow.contains(nextRange.lowerBound)) { return Maps.immutableEntry(nextRange.lowerBound, nextRange); } else { return endOfData(); } } }; } @Override public int size() { return Iterators.size(entryIterator()); } } @Override public RangeSet<C> subRangeSet(Range<C> view) { return view.equals(Range.<C>all()) ? this : new SubRangeSet(view); } private final class SubRangeSet extends TreeRangeSet<C> { private final Range<C> restriction; SubRangeSet(Range<C> restriction) { super(new SubRangeSetRangesByLowerBound<C>( Range.<Cut<C>>all(), restriction, TreeRangeSet.this.rangesByLowerBound)); this.restriction = restriction; } @Override public boolean encloses(Range<C> range) { if (!restriction.isEmpty() && restriction.encloses(range)) { Range<C> enclosing = TreeRangeSet.this.rangeEnclosing(range); return enclosing != null && !enclosing.intersection(restriction).isEmpty(); } return false; } @Override @Nullable public Range<C> rangeContaining(C value) { if (!restriction.contains(value)) { return null; } Range<C> result = TreeRangeSet.this.rangeContaining(value); return (result == null) ? null : result.intersection(restriction); } @Override public void add(Range<C> rangeToAdd) { checkArgument(restriction.encloses(rangeToAdd), "Cannot add range %s to subRangeSet(%s)", rangeToAdd, restriction); super.add(rangeToAdd); } @Override public void remove(Range<C> rangeToRemove) { if (rangeToRemove.isConnected(restriction)) { TreeRangeSet.this.remove(rangeToRemove.intersection(restriction)); } } @Override public boolean contains(C value) { return restriction.contains(value) && TreeRangeSet.this.contains(value); } @Override public void clear() { TreeRangeSet.this.remove(restriction); } @Override public RangeSet<C> subRangeSet(Range<C> view) { if (view.encloses(restriction)) { return this; } else if (view.isConnected(restriction)) { return new SubRangeSet(restriction.intersection(view)); } else { return ImmutableRangeSet.of(); } } } }