/*
* Copyright 2015 Lukas Krejci
*
* 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 org.revapi;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Heavily inspired by the equivalently named class in Clirr 0.6.
*
* <p>This is an iterator that walks a pair of collections, returning
* matching pairs from the set.
*
* <p>When an element is present in the left set but there is no equal object
* in the right set, the pair (leftobj, null) is returned.
*
* <p>When an element is present in the right set but there is no equal object
* in the left set, the pair (null, rightobj) is returned.
*
* <p>When an element in one set has an equal element in the other set, the
* pair (leftobj, rightobj) is returned.
*
* <p>Note that the phrase "pair is returned" above actually means that the
* getLeft and getRight methods on the iterator return those objects; the
* pair is "conceptual" rather than a physical Pair instance. This avoids
* instantiating an object to represent the pair for each step of the
* iterator which would not be efficient.
*
* <p>Note also that elements from the sets are always returned in the
* iteration order.
*
* @author Simon Kitching
* @author Lukas Krejci
* @since 0.1
*/
public final class CoIterator<E> {
private final Iterator<? extends E> left;
private final Iterator<? extends E> right;
private final Comparator<? super E> comparator;
private E currentLeft;
private E currentRight;
private E reportedLeft;
private E reportedRight;
/**
* The iterators must iterate over sorted collections otherwise this instance might not
* produce the intended results.
*
* <p>Also, the iterators must not ever return null - i.e. the collections must not contain null
* values otherwise the behavior of the iteration is undefined.
*
* @param left the iterator over "left" collection
* @param right the iterator over "right" collection
* @param comparator the comparator used to sort the collections (this must have been done prior to calling this
* constructor)
*/
public CoIterator(Iterator<? extends E> left, Iterator<? extends E> right, Comparator<? super E> comparator) {
this.left = left;
this.right = right;
this.comparator = comparator;
if (left.hasNext()) {
currentLeft = left.next();
}
if (right.hasNext()) {
currentRight = right.next();
}
}
/**
* Assumes the iterators iterate over comparable elements and uses their natural ordering instead of an explicit
* comparator.
* <p>If <code>E</code> is not at the same time comparable, calling {@link #next()} will fail with a class cast
* exception at the first mutual comparison of elements from the two collections.
*
* @param left the iterator over the "left" collection
* @param right the iterator over the "right" collection
*
* @see #CoIterator(java.util.Iterator, java.util.Iterator, java.util.Comparator)
*/
public CoIterator(Iterator<? extends E> left, Iterator<? extends E> right) {
this(left, right, new NaturalOrderComparator());
}
public boolean hasNext() {
return currentLeft != null || currentRight != null;
}
/**
* Use {@link #getLeft()} and {@link #getRight()} to get the next elements from the two iterated collections.
*/
public void next() {
boolean hasLeft = currentLeft != null;
boolean hasRight = currentRight != null;
if (!hasLeft && !hasRight) {
throw new NoSuchElementException();
}
int order;
if (hasLeft && !hasRight) {
order = -1;
} else if (!hasLeft) {
order = 1;
} else {
order = comparator.compare(currentLeft, currentRight);
}
if (order < 0) {
reportedLeft = currentLeft;
currentLeft = nextOrNull(left);
reportedRight = null;
} else if (order > 0) {
reportedLeft = null;
reportedRight = currentRight;
currentRight = nextOrNull(right);
} else {
reportedLeft = currentLeft;
reportedRight = currentRight;
currentLeft = nextOrNull(left);
currentRight = nextOrNull(right);
}
}
/**
* After calling {@link #next()}, this will contain the next element from the "left" collection.
* @return the next element from the left collection
*/
public E getLeft() {
return reportedLeft;
}
/**
* After calling {@link #next()}, this will contain the next element from the "right" collection.
* @return the next element from the right collection
*/
public E getRight() {
return reportedRight;
}
private E nextOrNull(Iterator<? extends E> it) {
return it.hasNext() ? it.next() : null;
}
}