package mhfc.net.common.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Queue;
import com.google.common.collect.Lists;
/**
* <b>No thread safety guarantees.</b><br>
* A queue which iterates over an underlying iterator until it is consumed. It will only iterate when the head element
* of this queue is consumed itself, therefore it is lazy. Since the Java interface for Queues requires to support
* Collection as well, the queue will act like an infinitely huge collection as long as it is not empty. <br>
* Null elements are not allowed to appear in the Iterable.<br>
* Be careful with certain methods as their use results in the construction of a list thus draining all elements from
* this queue which is potentially infinitely big. These methods are:<br>
* <ul>
* <li>{@link LazyQueue#size()}</li>
* <li>{@link LazyQueue#contains(Object)}</li>
* <li>{@link LazyQueue#containsAll(Collection)}</li>
* <li>{@link LazyQueue#toArray()}</li>
* <li>{@link LazyQueue#toArray(Object[]))}</li>
* </ul>
* {@link LazyQueue#iterator()} will contruct an iterator that drains this queue but is still lazy.
*
* @author HeroicKatora
*
* @param <E>
*/
public class LazyQueue<E> implements Queue<E> {
Iterator<E> iter;
E bufferEl;
public LazyQueue(Iterator<E> iter) {
this.iter = iter;
}
public LazyQueue(Iterable<E> orig) {
this(orig.iterator());
}
private List<E> toList() {
return Lists.newArrayList(this.iterator());
}
@Override
public int size() {
return isEmpty() ? 0 : toList().size();
}
@Override
public boolean isEmpty() {
return !iter.hasNext() && bufferEl == null;
}
@Override
public boolean contains(Object o) {
return toList().contains(o);
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
LazyQueue<E> ref = LazyQueue.this;
@Override
public boolean hasNext() {
return !ref.isEmpty();
}
@Override
public E next() {
return ref.remove();
}
};
}
@Override
public Object[] toArray() {
return toList().toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return toList().toArray(a);
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(Collection<?> c) {
return toList().containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean add(E e) {
throw new IllegalStateException();
}
@Override
public boolean offer(E e) {
return false;
}
/**
* Advances the internal state and returns the previous first element.<br>
* Throws {@link NullPointerException} when the new element is null.
*/
private E advance() throws NullPointerException {
E storage = bufferEl;
bufferEl = iter.next();
if (bufferEl == null)
throw new NullPointerException("Null elements are not permitted");
return storage;
}
private void ensureState() throws NullPointerException, NoSuchElementException {
if (isEmpty())
throw new NoSuchElementException();
if (bufferEl == null) // This only happens at the first element
advance();
}
@Override
public E remove() {
ensureState();
return poll();
}
@Override
public E poll() {
try {
ensureState();
} catch (NoSuchElementException e) {
return null;
}
if (iter.hasNext()) {
return advance();
} else {
E last = bufferEl;
bufferEl = null;
return last;
}
}
@Override
public E element() {
ensureState();
return bufferEl;
}
@Override
public E peek() {
try {
ensureState();
} catch (NoSuchElementException e) {
return null;
}
return bufferEl;
}
}