/*
* The MIT License
*
* Copyright (c) 2009 The Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package htsjdk.samtools.util;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* A Red-Black tree with intervals for keys.
* Not thread-safe, and cannot be made so.
*
* 7/24/2008: This was copied from the tedUtils package.
* IMPORTANT!!! It has been modified to use the Reseq way of
* handling coordinates (end-inclusive).
*
* @author tsharpe
*/
public class IntervalTree<V> implements Iterable<IntervalTree.Node<V>>
{
/**
* Return the number of intervals in the tree.
* @return The number of intervals.
*/
public int size()
{
return mRoot == null ? 0 : mRoot.getSize();
}
/**
* Remove all entries.
*/
public void clear()
{
mRoot = null;
}
/**
* Put a new interval into the tree (or update the value associated with an existing interval).
* If the interval is novel, the special sentinel value is returned.
* @param start The interval's start.
* @param end The interval's end.
* @param value The associated value.
* @return The old value associated with that interval, or the sentinel.
*/
@SuppressWarnings("null")
public V put( final int start, final int end, final V value )
{
if ( start > end )
throw new IllegalArgumentException("Start cannot exceed end.");
V result = mSentinel;
if ( mRoot == null )
{
mRoot = new Node<V>(start,end,value);
}
else
{
Node<V> parent = null;
Node<V> node = mRoot;
int cmpVal = 0;
while ( node != null )
{
parent = node; // last non-null node
cmpVal = node.compare(start,end);
if ( cmpVal == 0 )
{
break;
}
node = cmpVal < 0 ? node.getLeft() : node.getRight();
}
if ( cmpVal == 0 )
{
result = parent.setValue(value);
}
else
{
if ( cmpVal < 0 )
{
mRoot = parent.insertLeft(start,end,value,mRoot);
}
else
{
mRoot = parent.insertRight(start,end,value,mRoot);
}
}
}
return result;
}
/**
* Remove an interval from the tree. If the interval does not exist in the tree the
* special sentinel value is returned.
* @param start The interval's start.
* @param end The interval's end.
* @return The value associated with that interval, or the sentinel.
*/
public V remove( final int start, final int end )
{
V result = mSentinel;
Node<V> node = mRoot;
while ( node != null )
{
final int cmpVal = node.compare(start,end);
if ( cmpVal == 0 )
{
result = node.getValue();
mRoot = node.remove(mRoot);
break;
}
node = cmpVal < 0 ? node.getLeft() : node.getRight();
}
return result;
}
/**
* Find an interval.
* @param start The interval's start.
* @param end The interval's end.
* @return The Node that represents that interval, or null.
*/
public Node<V> find( final int start, final int end )
{
Node<V> node = mRoot;
while ( node != null )
{
final int cmpVal = node.compare(start,end);
if ( cmpVal == 0 )
{
break;
}
node = cmpVal < 0 ? node.getLeft() : node.getRight();
}
return node;
}
/**
* Find the nth interval in the tree.
* @param idx The rank of the interval sought (from 0 to size()-1).
* @return The Node that represents the nth interval.
*/
public Node<V> findByIndex( final int idx )
{
return Node.findByRank(mRoot,idx+1);
}
/**
* Find the rank of the specified interval. If the specified interval is not in the
* tree, then -1 is returned.
* @param start The interval's start.
* @param end The interval's end.
* @return The rank of that interval, or -1.
*/
public int getIndex( final int start, final int end )
{
return Node.getRank(mRoot,start,end) - 1;
}
/**
* Find the least interval in the tree.
* @return The earliest interval, or null if the tree is empty.
*/
public Node<V> min()
{
Node<V> result = null;
Node<V> node = mRoot;
while ( node != null )
{
result = node;
node = node.getLeft();
}
return result;
}
/**
* Find the earliest interval in the tree greater than or equal to the specified interval.
* @param start The interval's start.
* @param end The interval's end.
* @return The earliest >= interval, or null if there is none.
*/
@SuppressWarnings("null")
public Node<V> min( final int start, final int end )
{
Node<V> result = null;
Node<V> node = mRoot;
int cmpVal = 0;
while ( node != null )
{
result = node;
cmpVal = node.compare(start,end);
if ( cmpVal == 0 )
{
break;
}
node = cmpVal < 0 ? node.getLeft() : node.getRight();
}
if ( cmpVal > 0 )
{
result = result.getNext();
}
return result;
}
/**
* Find the earliest interval in the tree that overlaps the specified interval.
* @param start The interval's start.
* @param end The interval's end.
* @return The earliest overlapping interval, or null if there is none.
*/
public Node<V> minOverlapper( final int start, final int end )
{
Node<V> result = null;
Node<V> node = mRoot;
if ( node != null && node.getMaxEnd() >= start )
{
while ( true )
{
if ( node.getStart() <= end && start <= node.getEnd() )
{ // this node overlaps. there might be a lesser overlapper down the left sub-tree.
// no need to consider the right sub-tree: even if there's an overlapper, if won't be minimal
result = node;
node = node.getLeft();
if ( node == null || node.getMaxEnd() < start )
break; // no left sub-tree or all nodes end too early
}
else
{ // no overlap. if there might be a left sub-tree overlapper, consider the left sub-tree.
final Node<V> left = node.getLeft();
if ( left != null && left.getMaxEnd() >= start )
{
node = left;
}
else
{ // left sub-tree cannot contain an overlapper. consider the right sub-tree.
if ( node.getStart() > end )
break; // everything in the right sub-tree is past the end of the query interval
node = node.getRight();
if ( node == null || node.getMaxEnd() < start )
break; // no right sub-tree or all nodes end too early
}
}
}
}
return result;
}
/**
* Find the greatest interval in the tree.
* @return The latest interval, or null if the tree is empty.
*/
public Node<V> max()
{
Node<V> result = null;
Node<V> node = mRoot;
while ( node != null )
{
result = node;
node = node.getRight();
}
return result;
}
/**
* Find the latest interval in the tree less than or equal to the specified interval.
* @param start The interval's start.
* @param end The interval's end.
* @return The latest >= interval, or null if there is none.
*/
@SuppressWarnings("null")
public Node<V> max( final int start, final int end )
{
Node<V> result = null;
Node<V> node = mRoot;
int cmpVal = 0;
while ( node != null )
{
result = node;
cmpVal = node.compare(start,end);
if ( cmpVal == 0 )
{
break;
}
node = cmpVal < 0 ? node.getLeft() : node.getRight();
}
if ( cmpVal < 0 )
{
result = result.getPrev();
}
return result;
}
/**
* Return an iterator over the entire tree.
* @return An iterator.
*/
public Iterator<Node<V>> iterator()
{
return new FwdIterator(min());
}
/**
* Return an iterator over all intervals greater than or equal to the specified interval.
* @param start The interval's start.
* @param end The interval's end.
* @return An iterator.
*/
public Iterator<Node<V>> iterator( final int start, final int end )
{
return new FwdIterator(min(start,end));
}
/**
* Return an iterator over all intervals overlapping the specified range.
* @param start The range start.
* @param end The range end.
* @return An iterator.
*/
public Iterator<Node<V>> overlappers( final int start, final int end )
{
return new OverlapIterator(start,end);
}
/**
* Return an iterator over the entire tree that returns intervals in reverse order.
* @return An iterator.
*/
public Iterator<Node<V>> reverseIterator()
{
return new RevIterator(max());
}
/**
* Return an iterator over all intervals less than or equal to the specified interval, in reverse order.
* @param start The interval's start.
* @param end The interval's end.
* @return An iterator.
*/
public Iterator<Node<V>> reverseIterator( final int start, final int end )
{
return new RevIterator(max(start,end));
}
/**
* Get the special sentinel value that will be used to signal novelty when putting a new interval
* into the tree, or to signal "not found" when removing an interval. This is null by default.
* @return The sentinel value.
*/
public V getSentinel()
{
return mSentinel;
}
/**
* Set the special sentinel value that will be used to signal novelty when putting a new interval
* into the tree, or to signal "not found" when removing an interval.
* @param sentinel The new sentinel value.
* @return The old sentinel value.
*/
public V setSentinel( final V sentinel )
{
final V result = mSentinel;
mSentinel = sentinel;
return result;
}
/**
* This method is only for debugging.
* It verifies whether the tree is internally consistent with respect to the mMaxEnd cached on each node.
* @throws IllegalStateException If an inconsistency is detected.
*/
public void checkMaxEnds() {
if (mRoot != null) mRoot.checkMaxEnd();
}
/**
* This method draws a nested picture of the tree on System.out.
* Useful for debugging.
*/
public void printTree() {
if (mRoot != null) mRoot.printNode();
}
void removeNode( final Node<V> node )
{
mRoot = node.remove(mRoot);
}
private Node<V> mRoot;
private V mSentinel;
public static class Node<V1>
{
// bit-wise definitions from which the other constants are composed
public static final int HAS_LESSER_PART = 1;
public static final int HAS_OVERLAPPING_PART = 2;
public static final int HAS_GREATER_PART = 4;
public static final int IS_ADJACENT_AND_EMPTY = 0;
public static final int IS_STRICTLY_LESS = HAS_LESSER_PART; // 1
public static final int IS_SUBSET = HAS_OVERLAPPING_PART; // 2
public static final int IS_LEFT_OVERHANGING_OVERLAPPER = HAS_LESSER_PART | HAS_OVERLAPPING_PART; // 3
public static final int IS_STRICTLY_GREATER = HAS_GREATER_PART; // 4
// there is no value that equals 5, since that would imply overhanging on left and right without overlapping
public static final int IS_RIGHT_OVERHANGING_OVERLAPPER = HAS_GREATER_PART | HAS_OVERLAPPING_PART; // 6
public static final int IS_SUPERSET = HAS_LESSER_PART | HAS_OVERLAPPING_PART | HAS_GREATER_PART; // 7
Node( final int start, final int end, final V1 value )
{
mStart = start;
mEnd = end;
mValue = value;
mSize = 1;
mMaxEnd = mEnd;
mIsBlack = true;
}
Node( final Node<V1> parent, final int start, final int end, final V1 value )
{
mParent = parent;
mStart = start;
mEnd = end;
mValue = value;
mMaxEnd = mEnd;
mSize = 1;
}
public int getStart()
{
return mStart;
}
public int getEnd()
{
return mEnd;
}
public int getLength()
{
return mEnd - mStart;
}
public int getRelationship( final Node<V1> interval )
{
int result = 0;
if ( mStart < interval.getStart() )
result = HAS_LESSER_PART;
if ( mEnd > interval.getEnd() )
result |= HAS_GREATER_PART;
if ( mStart < interval.getEnd() && interval.getStart() < mEnd )
result |= HAS_OVERLAPPING_PART;
return result;
}
public boolean isAdjacent( final Node<V1> interval )
{
return mStart == interval.getEnd() || mEnd == interval.getStart();
}
public V1 getValue()
{
return mValue;
}
public V1 setValue( final V1 value )
{
final V1 result = mValue;
mValue = value;
return result;
}
int getSize()
{
return mSize;
}
int getMaxEnd()
{
return mMaxEnd;
}
Node<V1> getLeft()
{
return mLeft;
}
Node<V1> insertLeft( final int start, final int end, final V1 value, final Node<V1> root )
{
mLeft = new Node<V1>(this,start,end,value);
return insertFixup(mLeft,root);
}
Node<V1> getRight()
{
return mRight;
}
Node<V1> insertRight( final int start, final int end, final V1 value, final Node<V1> root )
{
mRight = new Node<V1>(this,start,end,value);
return insertFixup(mRight,root);
}
Node<V1> getNext()
{
Node<V1> result;
if ( mRight != null )
{
result = mRight;
while ( result.mLeft != null )
{
result = result.mLeft;
}
}
else
{
Node<V1> node = this;
result = mParent;
while ( result != null && node == result.mRight )
{
node = result;
result = result.mParent;
}
}
return result;
}
Node<V1> getPrev()
{
Node<V1> result;
if ( mLeft != null )
{
result = mLeft;
while ( result.mRight != null )
{
result = result.mRight;
}
}
else
{
Node<V1> node = this;
result = mParent;
while ( result != null && node == result.mLeft )
{
node = result;
result = result.mParent;
}
}
return result;
}
boolean wasRemoved()
{
return mSize == 0;
}
Node<V1> remove( Node<V1> root )
{
if ( mSize == 0 )
{
throw new IllegalStateException("Entry was already removed.");
}
if ( mLeft == null )
{
if ( mRight == null )
{ // no children
if ( mParent == null )
{
root = null;
}
else if ( mParent.mLeft == this )
{
mParent.mLeft = null;
fixup(mParent);
if ( mIsBlack )
root = removeFixup(mParent,null,root);
}
else
{
mParent.mRight = null;
fixup(mParent);
if ( mIsBlack )
root = removeFixup(mParent,null,root);
}
}
else
{ // single child on right
root = spliceOut(mRight,root);
}
}
else if ( mRight == null )
{ // single child on left
root = spliceOut(mLeft,root);
}
else
{ // two children
final Node<V1> next = getNext();
root = next.remove(root);
// put next into tree in same position as this, effectively removing this
if ( (next.mParent = mParent) == null )
root = next;
else if ( mParent.mLeft == this )
mParent.mLeft = next;
else
mParent.mRight = next;
if ( (next.mLeft = mLeft) != null )
{
mLeft.mParent = next;
}
if ( (next.mRight = mRight) != null )
{
mRight.mParent = next;
}
next.mIsBlack = mIsBlack;
next.mSize = mSize;
// PIC-123 fix
fixup(next);
}
mSize = 0;
return root;
}
// backwards comparison! compares start+end to this.
int compare( final int start, final int end )
{
int result = 0;
if ( start > mStart )
result = 1;
else if ( start < mStart )
result = -1;
else if ( end > mEnd )
result = 1;
else if ( end < mEnd )
result = -1;
return result;
}
@SuppressWarnings("null")
static <V1> Node<V1> getNextOverlapper( Node<V1> node, final int start, final int end )
{
do
{
Node<V1> nextNode = node.mRight;
if ( nextNode != null && nextNode.mMaxEnd >= start )
{
node = nextNode;
while ( (nextNode = node.mLeft) != null && nextNode.mMaxEnd >= start )
node = nextNode;
}
else
{
nextNode = node;
while ( (node = nextNode.mParent) != null && node.mRight == nextNode )
nextNode = node;
}
if ( node != null && node.mStart > end )
node = null;
}
while ( node != null && !(node.mStart <= end && start <= node.mEnd) );
return node;
}
static <V1> Node<V1> findByRank( Node<V1> node, int rank )
{
while ( node != null )
{
final int nodeRank = node.getRank();
if ( rank == nodeRank )
break;
if ( rank < nodeRank )
{
node = node.mLeft;
}
else
{
node = node.mRight;
rank -= nodeRank;
}
}
return node;
}
static <V1> int getRank( Node<V1> node, final int start, final int end )
{
int rank = 0;
while ( node != null )
{
final int cmpVal = node.compare(start,end);
if ( cmpVal < 0 )
{
node = node.mLeft;
}
else
{
rank += node.getRank();
if ( cmpVal == 0 )
return rank; // EARLY RETURN!!!
node = node.mRight;
}
}
return 0;
}
private int getRank()
{
int result = 1;
if ( mLeft != null )
result = mLeft.mSize + 1;
return result;
}
private Node<V1> spliceOut( final Node<V1> child, Node<V1> root )
{
if ( (child.mParent = mParent) == null )
{
root = child;
child.mIsBlack = true;
}
else
{
if ( mParent.mLeft == this )
mParent.mLeft = child;
else
mParent.mRight = child;
fixup(mParent);
if ( mIsBlack )
root = removeFixup(mParent,child,root);
}
return root;
}
private Node<V1> rotateLeft( Node<V1> root )
{
final Node<V1> child = mRight;
final int childSize = child.mSize;
child.mSize = mSize;
mSize -= childSize;
if ( (mRight = child.mLeft) != null )
{
mRight.mParent = this;
mSize += mRight.mSize;
}
if ( (child.mParent = mParent) == null )
root = child;
else if ( this == mParent.mLeft )
mParent.mLeft = child;
else
mParent.mRight = child;
child.mLeft = this;
mParent = child;
setMaxEnd();
child.setMaxEnd();
return root;
}
private Node<V1> rotateRight( Node<V1> root )
{
final Node<V1> child = mLeft;
final int childSize = child.mSize;
child.mSize = mSize;
mSize -= childSize;
if ( (mLeft = child.mRight) != null )
{
mLeft.mParent = this;
mSize += mLeft.mSize;
}
if ( (child.mParent = mParent) == null )
root = child;
else if ( this == mParent.mLeft )
mParent.mLeft = child;
else
mParent.mRight = child;
child.mRight = this;
mParent = child;
setMaxEnd();
child.setMaxEnd();
return root;
}
private void setMaxEnd()
{
mMaxEnd = mEnd;
if ( mLeft != null )
mMaxEnd = Math.max(mMaxEnd,mLeft.mMaxEnd);
if ( mRight != null )
mMaxEnd = Math.max(mMaxEnd,mRight.mMaxEnd);
}
private static <V1> void fixup( Node<V1> node )
{
do
{
node.mSize = 1;
node.mMaxEnd = node.mEnd;
if ( node.mLeft != null )
{
node.mSize += node.mLeft.mSize;
node.mMaxEnd = Math.max(node.mMaxEnd,node.mLeft.mMaxEnd);
}
if ( node.mRight != null )
{
node.mSize += node.mRight.mSize;
node.mMaxEnd = Math.max(node.mMaxEnd,node.mRight.mMaxEnd);
}
}
while ( (node = node.mParent) != null );
}
private static <V1> Node<V1> insertFixup( Node<V1> daughter, Node<V1> root )
{
Node<V1> mom = daughter.mParent;
fixup(mom);
while( mom != null && !mom.mIsBlack )
{
final Node<V1> gramma = mom.mParent;
Node<V1> auntie = gramma.mLeft;
if ( auntie == mom )
{
auntie = gramma.mRight;
if ( auntie != null && !auntie.mIsBlack )
{
mom.mIsBlack = true;
auntie.mIsBlack = true;
gramma.mIsBlack = false;
daughter = gramma;
}
else
{
if ( daughter == mom.mRight )
{
root = mom.rotateLeft(root);
mom = daughter;
}
mom.mIsBlack = true;
gramma.mIsBlack = false;
root = gramma.rotateRight(root);
break;
}
}
else
{
if ( auntie != null && !auntie.mIsBlack )
{
mom.mIsBlack = true;
auntie.mIsBlack = true;
gramma.mIsBlack = false;
daughter = gramma;
}
else
{
if ( daughter == mom.mLeft )
{
root = mom.rotateRight(root);
mom = daughter;
}
mom.mIsBlack = true;
gramma.mIsBlack = false;
root = gramma.rotateLeft(root);
break;
}
}
mom = daughter.mParent;
}
root.mIsBlack = true;
return root;
}
private static <V1> Node<V1> removeFixup( Node<V1> parent, Node<V1> node, Node<V1> root )
{
do
{
if ( node == parent.mLeft )
{
Node<V1> sister = parent.mRight;
if ( !sister.mIsBlack )
{
sister.mIsBlack = true;
parent.mIsBlack = false;
root = parent.rotateLeft(root);
sister = parent.mRight;
}
if ( (sister.mLeft == null || sister.mLeft.mIsBlack) && (sister.mRight == null || sister.mRight.mIsBlack) )
{
sister.mIsBlack = false;
node = parent;
}
else
{
if ( sister.mRight == null || sister.mRight.mIsBlack )
{
sister.mLeft.mIsBlack = true;
sister.mIsBlack = false;
root = sister.rotateRight(root);
sister = parent.mRight;
}
sister.mIsBlack = parent.mIsBlack;
parent.mIsBlack = true;
sister.mRight.mIsBlack = true;
root = parent.rotateLeft(root);
node = root;
}
}
else
{
Node<V1> sister = parent.mLeft;
if ( !sister.mIsBlack )
{
sister.mIsBlack = true;
parent.mIsBlack = false;
root = parent.rotateRight(root);
sister = parent.mLeft;
}
if ( (sister.mLeft == null || sister.mLeft.mIsBlack) && (sister.mRight == null || sister.mRight.mIsBlack) )
{
sister.mIsBlack = false;
node = parent;
}
else
{
if ( sister.mLeft == null || sister.mLeft.mIsBlack )
{
sister.mRight.mIsBlack = true;
sister.mIsBlack = false;
root = sister.rotateLeft(root);
sister = parent.mLeft;
}
sister.mIsBlack = parent.mIsBlack;
parent.mIsBlack = true;
sister.mLeft.mIsBlack = true;
root = parent.rotateRight(root);
node = root;
}
}
parent = node.mParent;
}
while ( parent != null && node.mIsBlack );
node.mIsBlack = true;
return root;
}
public void checkMaxEnd() {
if (mMaxEnd != calcMaxEnd()) {
throw new IllegalStateException("Max end mismatch " + mMaxEnd + " vs " + calcMaxEnd() + ": " + this);
}
if (mLeft != null) mLeft.checkMaxEnd();
if (mRight != null) mRight.checkMaxEnd();
}
private int calcMaxEnd() {
int end = mEnd;
if (mLeft != null) end = Math.max(end, mLeft.mMaxEnd);
if (mRight != null) end = Math.max(end, mRight.mMaxEnd);
return end;
}
public void printNode() {
this.printNodeInternal("", "root: ");
}
private void printNodeInternal(final String padding, final String tag) {
System.out.println(padding + tag + " " + this);
if (mLeft != null) mLeft.printNodeInternal(padding + " ", "left: ");
if (mRight != null) mRight.printNodeInternal(padding + " ", "right:");
}
public String toString() {
return "Node(" + mStart + "," + mEnd + "," + mValue + "," + mSize + "," + mMaxEnd + "," + mIsBlack + ")";
}
private Node<V1> mParent;
private Node<V1> mLeft;
private Node<V1> mRight;
private final int mStart;
private final int mEnd;
private V1 mValue;
private int mSize;
private int mMaxEnd;
private boolean mIsBlack;
}
public class FwdIterator
implements Iterator<Node<V>>
{
public FwdIterator( final Node<V> node )
{
mNext = node;
}
public boolean hasNext()
{
return mNext != null;
}
public Node<V> next()
{
if ( mNext == null )
{
throw new NoSuchElementException("No next element.");
}
if ( mNext.wasRemoved() )
{
mNext = min(mNext.getStart(),mNext.getEnd());
if ( mNext == null )
throw new ConcurrentModificationException("Current element was removed, and there are no more elements.");
}
mLast = mNext;
mNext = mNext.getNext();
return mLast;
}
public void remove()
{
if ( mLast == null )
{
throw new IllegalStateException("No entry to remove.");
}
removeNode(mLast);
mLast = null;
}
private Node<V> mNext;
private Node<V> mLast;
}
public class RevIterator
implements Iterator<Node<V>>
{
public RevIterator( final Node<V> node )
{
mNext = node;
}
public boolean hasNext()
{
return mNext != null;
}
public Node<V> next()
{
if ( mNext == null )
throw new NoSuchElementException("No next element.");
if ( mNext.wasRemoved() )
{
mNext = max(mNext.getStart(),mNext.getEnd());
if ( mNext == null )
throw new ConcurrentModificationException("Current element was removed, and there are no more elements.");
}
mLast = mNext;
mNext = mNext.getPrev();
return mLast;
}
public void remove()
{
if ( mLast == null )
{
throw new IllegalStateException("No entry to remove.");
}
removeNode(mLast);
mLast = null;
}
private Node<V> mNext;
private Node<V> mLast;
}
public class OverlapIterator
implements Iterator<Node<V>>
{
public OverlapIterator( final int start, final int end )
{
mNext = minOverlapper(start,end);
mStart = start;
mEnd = end;
}
public boolean hasNext()
{
return mNext != null;
}
public Node<V> next()
{
if ( mNext == null )
{
throw new NoSuchElementException("No next element.");
}
if ( mNext.wasRemoved() )
{
throw new ConcurrentModificationException("Current element was removed.");
}
mLast = mNext;
mNext = Node.getNextOverlapper(mNext,mStart,mEnd);
return mLast;
}
public void remove()
{
if ( mLast == null )
{
throw new IllegalStateException("No entry to remove.");
}
removeNode(mLast);
mLast = null;
}
private Node<V> mNext;
private Node<V> mLast;
private final int mStart;
private final int mEnd;
}
public static class ValuesIterator<V1>
implements Iterator<V1>
{
public ValuesIterator( final Iterator<Node<V1>> itr )
{
mItr = itr;
}
public boolean hasNext()
{
return mItr.hasNext();
}
public V1 next()
{
return mItr.next().getValue();
}
public void remove()
{
mItr.remove();
}
private final Iterator<Node<V1>> mItr;
}
}