package ch.akuhn.foreach;
import static ch.akuhn.foreach.State.NULL;
import static ch.akuhn.foreach.State.VOID;
import static ch.akuhn.foreach.State.YIELD;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public final class Select<E> implements Iterable<EachB<E>> {
public static <E> Select<E> from(E... elements) {
return new Select<E>(Arrays.asList(elements), elements.getClass().getComponentType());
}
public static <E> Select<E> from(Iterable<E> elements) {
return new Select<E>(elements, null);
}
private Class<?> type;
private Iter iter;
private Select(Iterable<E> elements, Class<?> type) {
this.iter = new Iter(elements.iterator());
this.type = type;
}
public List<E> getResult() {
return iter.getResult();
}
@SuppressWarnings("unchecked")
public E[] getResultArray() {
List<E> result = getResult();
if (type == null) type = result.iterator().next().getClass();
return result.toArray((E[]) Array.newInstance(type, result.size()));
}
public Iterator<EachB<E>> iterator() {
return iter.start();
}
private class Iter implements Iterator<EachB<E>> {
private EachB<E> each;
private Iterator<E> elements;
private int index = 0;
private List<E> result;
private State state;
private Iter(Iterator<E> elements) {
state = NULL;
this.elements = elements;
}
private List<E> getResult() {
if (state == YIELD) hasNext();
return result;
}
public boolean hasNext() {
if (state == YIELD) {
if (each.yield) result.add(each.value);
state = VOID;
}
if (elements.hasNext()) return true;
elements = null;
return false;
}
public EachB<E> next() {
if (!hasNext()) throw new NoSuchElementException();
each.value = elements.next();
each.yield = false;
each.index = index++;
state = YIELD;
return each;
}
public void remove() {
throw new UnsupportedOperationException();
}
private Iter start() {
if (state != NULL) throw new IllegalStateException("Cannot run query twice!");
state = VOID;
each = new EachB<E>();
result = new ArrayList<E>();
index = 0;
return this;
}
}
}