All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.TreeRangeSet Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 33.0.2.Final
Show newest version
/*
 * 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.io.Serializable;
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 org.checkerframework.checker.nullness.compatqual.MonotonicNonNullDecl;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
 * An implementation of {@link RangeSet} backed by a {@link TreeMap}.
 *
 * @author Louis Wasserman
 * @since 14.0
 */
@Beta
@GwtIncompatible // uses NavigableMap
public class TreeRangeSet> extends AbstractRangeSet
    implements Serializable {

  @VisibleForTesting final NavigableMap, Range> rangesByLowerBound;

  /** Creates an empty {@code TreeRangeSet} instance. */
  public static > TreeRangeSet create() {
    return new TreeRangeSet(new TreeMap, Range>());
  }

  /** Returns a {@code TreeRangeSet} initialized with the ranges in the specified range set. */
  public static > TreeRangeSet create(RangeSet rangeSet) {
    TreeRangeSet result = create();
    result.addAll(rangeSet);
    return result;
  }

  /**
   * Returns a {@code TreeRangeSet} representing the union of the specified ranges.
   *
   * 

This is the smallest {@code RangeSet} which encloses each of the specified ranges. An * element will be contained in this {@code RangeSet} if and only if it is contained in at least * one {@code Range} in {@code ranges}. * * @since 21.0 */ public static > TreeRangeSet create(Iterable> ranges) { TreeRangeSet result = create(); result.addAll(ranges); return result; } private TreeRangeSet(NavigableMap, Range> rangesByLowerCut) { this.rangesByLowerBound = rangesByLowerCut; } @MonotonicNonNullDecl private transient Set> asRanges; @MonotonicNonNullDecl private transient Set> asDescendingSetOfRanges; @Override public Set> asRanges() { Set> result = asRanges; return (result == null) ? asRanges = new AsRanges(rangesByLowerBound.values()) : result; } @Override public Set> asDescendingSetOfRanges() { Set> result = asDescendingSetOfRanges; return (result == null) ? asDescendingSetOfRanges = new AsRanges(rangesByLowerBound.descendingMap().values()) : result; } final class AsRanges extends ForwardingCollection> implements Set> { final Collection> delegate; AsRanges(Collection> delegate) { this.delegate = delegate; } @Override protected Collection> delegate() { return delegate; } @Override public int hashCode() { return Sets.hashCodeImpl(this); } @Override public boolean equals(@NullableDecl Object o) { return Sets.equalsImpl(this, o); } } @Override @NullableDecl public Range rangeContaining(C value) { checkNotNull(value); Entry, Range> 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 intersects(Range range) { checkNotNull(range); Entry, Range> ceilingEntry = rangesByLowerBound.ceilingEntry(range.lowerBound); if (ceilingEntry != null && ceilingEntry.getValue().isConnected(range) && !ceilingEntry.getValue().intersection(range).isEmpty()) { return true; } Entry, Range> priorEntry = rangesByLowerBound.lowerEntry(range.lowerBound); return priorEntry != null && priorEntry.getValue().isConnected(range) && !priorEntry.getValue().intersection(range).isEmpty(); } @Override public boolean encloses(Range range) { checkNotNull(range); Entry, Range> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); return floorEntry != null && floorEntry.getValue().encloses(range); } @NullableDecl private Range rangeEnclosing(Range range) { checkNotNull(range); Entry, Range> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); return (floorEntry != null && floorEntry.getValue().encloses(range)) ? floorEntry.getValue() : null; } @Override public Range span() { Entry, Range> firstEntry = rangesByLowerBound.firstEntry(); Entry, Range> lastEntry = rangesByLowerBound.lastEntry(); if (firstEntry == null) { throw new NoSuchElementException(); } return Range.create(firstEntry.getValue().lowerBound, lastEntry.getValue().upperBound); } @Override public void add(Range rangeToAdd) { checkNotNull(rangeToAdd); if (rangeToAdd.isEmpty()) { return; } // We will use { } to illustrate ranges currently in the range set, and < > // to illustrate rangeToAdd. Cut lbToAdd = rangeToAdd.lowerBound; Cut ubToAdd = rangeToAdd.upperBound; Entry, Range> entryBelowLB = rangesByLowerBound.lowerEntry(lbToAdd); if (entryBelowLB != null) { // { < Range 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, Range> entryBelowUB = rangesByLowerBound.floorEntry(ubToAdd); if (entryBelowUB != null) { // { > Range 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 rangeToRemove) { checkNotNull(rangeToRemove); if (rangeToRemove.isEmpty()) { return; } // We will use { } to illustrate ranges currently in the range set, and < > // to illustrate rangeToRemove. Entry, Range> entryBelowLB = rangesByLowerBound.lowerEntry(rangeToRemove.lowerBound); if (entryBelowLB != null) { // { < Range 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, Range> entryBelowUB = rangesByLowerBound.floorEntry(rangeToRemove.upperBound); if (entryBelowUB != null) { // { > Range 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 range) { if (range.isEmpty()) { rangesByLowerBound.remove(range.lowerBound); } else { rangesByLowerBound.put(range.lowerBound, range); } } @MonotonicNonNullDecl private transient RangeSet complement; @Override public RangeSet complement() { RangeSet result = complement; return (result == null) ? complement = new Complement() : result; } @VisibleForTesting static final class RangesByUpperBound> extends AbstractNavigableMap, Range> { private final NavigableMap, Range> 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> upperBoundWindow; RangesByUpperBound(NavigableMap, Range> rangesByLowerBound) { this.rangesByLowerBound = rangesByLowerBound; this.upperBoundWindow = Range.all(); } private RangesByUpperBound( NavigableMap, Range> rangesByLowerBound, Range> upperBoundWindow) { this.rangesByLowerBound = rangesByLowerBound; this.upperBoundWindow = upperBoundWindow; } private NavigableMap, Range> subMap(Range> window) { if (window.isConnected(upperBoundWindow)) { return new RangesByUpperBound(rangesByLowerBound, window.intersection(upperBoundWindow)); } else { return ImmutableSortedMap.of(); } } @Override public NavigableMap, Range> subMap( Cut fromKey, boolean fromInclusive, Cut toKey, boolean toInclusive) { return subMap( Range.range( fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); } @Override public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); } @Override public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); } @Override public Comparator> comparator() { return Ordering.>natural(); } @Override public boolean containsKey(@NullableDecl Object key) { return get(key) != null; } @Override public Range get(@NullableDecl Object key) { if (key instanceof Cut) { try { @SuppressWarnings("unchecked") // we catch CCEs Cut cut = (Cut) key; if (!upperBoundWindow.contains(cut)) { return null; } Entry, Range> candidate = rangesByLowerBound.lowerEntry(cut); if (candidate != null && candidate.getValue().upperBound.equals(cut)) { return candidate.getValue(); } } catch (ClassCastException e) { return null; } } return null; } @Override Iterator, Range>> entryIterator() { /* * We want to start the iteration at the first range where the upper bound is in * upperBoundWindow. */ final Iterator> backingItr; if (!upperBoundWindow.hasLowerBound()) { backingItr = rangesByLowerBound.values().iterator(); } else { Entry, Range> 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, Range>>() { @Override protected Entry, Range> computeNext() { if (!backingItr.hasNext()) { return endOfData(); } Range range = backingItr.next(); if (upperBoundWindow.upperBound.isLessThan(range.upperBound)) { return endOfData(); } else { return Maps.immutableEntry(range.upperBound, range); } } }; } @Override Iterator, Range>> descendingEntryIterator() { Collection> candidates; if (upperBoundWindow.hasUpperBound()) { candidates = rangesByLowerBound .headMap(upperBoundWindow.upperEndpoint(), false) .descendingMap() .values(); } else { candidates = rangesByLowerBound.descendingMap().values(); } final PeekingIterator> backingItr = Iterators.peekingIterator(candidates.iterator()); if (backingItr.hasNext() && upperBoundWindow.upperBound.isLessThan(backingItr.peek().upperBound)) { backingItr.next(); } return new AbstractIterator, Range>>() { @Override protected Entry, Range> computeNext() { if (!backingItr.hasNext()) { return endOfData(); } Range 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> extends AbstractNavigableMap, Range> { private final NavigableMap, Range> positiveRangesByLowerBound; private final NavigableMap, Range> 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> complementLowerBoundWindow; ComplementRangesByLowerBound(NavigableMap, Range> positiveRangesByLowerBound) { this(positiveRangesByLowerBound, Range.>all()); } private ComplementRangesByLowerBound( NavigableMap, Range> positiveRangesByLowerBound, Range> window) { this.positiveRangesByLowerBound = positiveRangesByLowerBound; this.positiveRangesByUpperBound = new RangesByUpperBound(positiveRangesByLowerBound); this.complementLowerBoundWindow = window; } private NavigableMap, Range> subMap(Range> subWindow) { if (!complementLowerBoundWindow.isConnected(subWindow)) { return ImmutableSortedMap.of(); } else { subWindow = subWindow.intersection(complementLowerBoundWindow); return new ComplementRangesByLowerBound(positiveRangesByLowerBound, subWindow); } } @Override public NavigableMap, Range> subMap( Cut fromKey, boolean fromInclusive, Cut toKey, boolean toInclusive) { return subMap( Range.range( fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); } @Override public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); } @Override public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); } @Override public Comparator> comparator() { return Ordering.>natural(); } @Override Iterator, Range>> 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> positiveRanges; if (complementLowerBoundWindow.hasLowerBound()) { positiveRanges = positiveRangesByUpperBound .tailMap( complementLowerBoundWindow.lowerEndpoint(), complementLowerBoundWindow.lowerBoundType() == BoundType.CLOSED) .values(); } else { positiveRanges = positiveRangesByUpperBound.values(); } final PeekingIterator> positiveItr = Iterators.peekingIterator(positiveRanges.iterator()); final Cut firstComplementRangeLowerBound; if (complementLowerBoundWindow.contains(Cut.belowAll()) && (!positiveItr.hasNext() || positiveItr.peek().lowerBound != Cut.belowAll())) { firstComplementRangeLowerBound = Cut.belowAll(); } else if (positiveItr.hasNext()) { firstComplementRangeLowerBound = positiveItr.next().upperBound; } else { return Iterators.emptyIterator(); } return new AbstractIterator, Range>>() { Cut nextComplementRangeLowerBound = firstComplementRangeLowerBound; @Override protected Entry, Range> computeNext() { if (complementLowerBoundWindow.upperBound.isLessThan(nextComplementRangeLowerBound) || nextComplementRangeLowerBound == Cut.aboveAll()) { return endOfData(); } Range negativeRange; if (positiveItr.hasNext()) { Range positiveRange = positiveItr.next(); negativeRange = Range.create(nextComplementRangeLowerBound, positiveRange.lowerBound); nextComplementRangeLowerBound = positiveRange.upperBound; } else { negativeRange = Range.create(nextComplementRangeLowerBound, Cut.aboveAll()); nextComplementRangeLowerBound = Cut.aboveAll(); } return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); } }; } @Override Iterator, Range>> 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 startingPoint = complementLowerBoundWindow.hasUpperBound() ? complementLowerBoundWindow.upperEndpoint() : Cut.aboveAll(); boolean inclusive = complementLowerBoundWindow.hasUpperBound() && complementLowerBoundWindow.upperBoundType() == BoundType.CLOSED; final PeekingIterator> positiveItr = Iterators.peekingIterator( positiveRangesByUpperBound .headMap(startingPoint, inclusive) .descendingMap() .values() .iterator()); Cut cut; if (positiveItr.hasNext()) { cut = (positiveItr.peek().upperBound == Cut.aboveAll()) ? positiveItr.next().lowerBound : positiveRangesByLowerBound.higherKey(positiveItr.peek().upperBound); } else if (!complementLowerBoundWindow.contains(Cut.belowAll()) || positiveRangesByLowerBound.containsKey(Cut.belowAll())) { return Iterators.emptyIterator(); } else { cut = positiveRangesByLowerBound.higherKey(Cut.belowAll()); } final Cut firstComplementRangeUpperBound = MoreObjects.firstNonNull(cut, Cut.aboveAll()); return new AbstractIterator, Range>>() { Cut nextComplementRangeUpperBound = firstComplementRangeUpperBound; @Override protected Entry, Range> computeNext() { if (nextComplementRangeUpperBound == Cut.belowAll()) { return endOfData(); } else if (positiveItr.hasNext()) { Range positiveRange = positiveItr.next(); Range 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.belowAll())) { Range negativeRange = Range.create(Cut.belowAll(), nextComplementRangeUpperBound); nextComplementRangeUpperBound = Cut.belowAll(); return Maps.immutableEntry(Cut.belowAll(), negativeRange); } return endOfData(); } }; } @Override public int size() { return Iterators.size(entryIterator()); } @Override @NullableDecl public Range get(Object key) { if (key instanceof Cut) { try { @SuppressWarnings("unchecked") Cut cut = (Cut) key; // tailMap respects the current window Entry, Range> 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 { Complement() { super(new ComplementRangesByLowerBound(TreeRangeSet.this.rangesByLowerBound)); } @Override public void add(Range rangeToAdd) { TreeRangeSet.this.remove(rangeToAdd); } @Override public void remove(Range rangeToRemove) { TreeRangeSet.this.add(rangeToRemove); } @Override public boolean contains(C value) { return !TreeRangeSet.this.contains(value); } @Override public RangeSet complement() { return TreeRangeSet.this; } } private static final class SubRangeSetRangesByLowerBound> extends AbstractNavigableMap, Range> { /** * lowerBoundWindow is the headMap/subMap/tailMap view; it only restricts the keys, and does not * affect the values. */ private final Range> lowerBoundWindow; /** * restriction is the subRangeSet view; ranges are truncated to their intersection with * restriction. */ private final Range restriction; private final NavigableMap, Range> rangesByLowerBound; private final NavigableMap, Range> rangesByUpperBound; private SubRangeSetRangesByLowerBound( Range> lowerBoundWindow, Range restriction, NavigableMap, Range> rangesByLowerBound) { this.lowerBoundWindow = checkNotNull(lowerBoundWindow); this.restriction = checkNotNull(restriction); this.rangesByLowerBound = checkNotNull(rangesByLowerBound); this.rangesByUpperBound = new RangesByUpperBound(rangesByLowerBound); } private NavigableMap, Range> subMap(Range> window) { if (!window.isConnected(lowerBoundWindow)) { return ImmutableSortedMap.of(); } else { return new SubRangeSetRangesByLowerBound( lowerBoundWindow.intersection(window), restriction, rangesByLowerBound); } } @Override public NavigableMap, Range> subMap( Cut fromKey, boolean fromInclusive, Cut toKey, boolean toInclusive) { return subMap( Range.range( fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); } @Override public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); } @Override public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); } @Override public Comparator> comparator() { return Ordering.>natural(); } @Override public boolean containsKey(@NullableDecl Object key) { return get(key) != null; } @Override @NullableDecl public Range get(@NullableDecl Object key) { if (key instanceof Cut) { try { @SuppressWarnings("unchecked") // we catch CCE's Cut cut = (Cut) 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 candidate = Maps.valueOrNull(rangesByLowerBound.floorEntry(cut)); if (candidate != null && candidate.upperBound.compareTo(restriction.lowerBound) > 0) { return candidate.intersection(restriction); } } else { Range result = rangesByLowerBound.get(cut); if (result != null) { return result.intersection(restriction); } } } catch (ClassCastException e) { return null; } } return null; } @Override Iterator, Range>> entryIterator() { if (restriction.isEmpty()) { return Iterators.emptyIterator(); } final Iterator> 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> upperBoundOnLowerBounds = Ordering.natural() .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); return new AbstractIterator, Range>>() { @Override protected Entry, Range> computeNext() { if (!completeRangeItr.hasNext()) { return endOfData(); } Range nextRange = completeRangeItr.next(); if (upperBoundOnLowerBounds.isLessThan(nextRange.lowerBound)) { return endOfData(); } else { nextRange = nextRange.intersection(restriction); return Maps.immutableEntry(nextRange.lowerBound, nextRange); } } }; } @Override Iterator, Range>> descendingEntryIterator() { if (restriction.isEmpty()) { return Iterators.emptyIterator(); } Cut> upperBoundOnLowerBounds = Ordering.natural() .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); final Iterator> completeRangeItr = rangesByLowerBound .headMap( upperBoundOnLowerBounds.endpoint(), upperBoundOnLowerBounds.typeAsUpperBound() == BoundType.CLOSED) .descendingMap() .values() .iterator(); return new AbstractIterator, Range>>() { @Override protected Entry, Range> computeNext() { if (!completeRangeItr.hasNext()) { return endOfData(); } Range 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 subRangeSet(Range view) { return view.equals(Range.all()) ? this : new SubRangeSet(view); } private final class SubRangeSet extends TreeRangeSet { private final Range restriction; SubRangeSet(Range restriction) { super( new SubRangeSetRangesByLowerBound( Range.>all(), restriction, TreeRangeSet.this.rangesByLowerBound)); this.restriction = restriction; } @Override public boolean encloses(Range range) { if (!restriction.isEmpty() && restriction.encloses(range)) { Range enclosing = TreeRangeSet.this.rangeEnclosing(range); return enclosing != null && !enclosing.intersection(restriction).isEmpty(); } return false; } @Override @NullableDecl public Range rangeContaining(C value) { if (!restriction.contains(value)) { return null; } Range result = TreeRangeSet.this.rangeContaining(value); return (result == null) ? null : result.intersection(restriction); } @Override public void add(Range rangeToAdd) { checkArgument( restriction.encloses(rangeToAdd), "Cannot add range %s to subRangeSet(%s)", rangeToAdd, restriction); super.add(rangeToAdd); } @Override public void remove(Range 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 subRangeSet(Range view) { if (view.encloses(restriction)) { return this; } else if (view.isConnected(restriction)) { return new SubRangeSet(restriction.intersection(view)); } else { return ImmutableRangeSet.of(); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy