package ch.akuhn.util;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* An iterator that yields its values one at a time. Subclasses must define a
* method called {@link #run()} and may call {@link yield(T)} to return values
* one at a time.
* <p>
* The generator ends when it reaches a return statement or the end of the
* method. On the other hand, an generator may run forever and thus yield an
* infinite sequence (see Example 1 for an example).
* <p>
* Please beware that calling {@link #hasNext()} on the generator (and thus any
* use in a for-each loop) provokes a lookahead of one value. Therefore you
* cannot repeatedly yield the same object, but rather, you must clone the value
* on each yield statement (see Example 3 for an example).
* <p>
* <b>Example 1:</b> Yields an infinite sequence of fibonacci numbers.
*
*<pre>
*Generator<Integer> fibonacci = new Generator<Integer>() {
* @Override
* public void run() {
* int a = 0, b = 1;
* while (true) {
* a = b + (b = a);
* yield(a);
* }
* }
*};
*for (int x : fibonacci) {
* if (x > 20000) break;
* System.out.println(x);
*}
*</pre>
* <p>
* <b>Example 2:</b> Yields all characters of the string "Hello, Worlds!".
*
*<pre>
*Generator<char>> hello = new Generator<char>() {
* @Override
* public void run() {
* String str = "Hello, Worlds!";
* for (int n = 0; n < str.length; n++) {
* yield(str.atChar(n));
* }
* }
*};
*for (char each : hello) {
* System.out.println(each);
*}
*</pre>
* <p>
* <b>Example 3:</b> Yields all perutations of an array.
*
*<pre>
*public static <T> Generator<T[]> permute(final T[] a) {
* return new Generator<T[]>() {
* @Override
* public void run() {
* permute(a.length - 1);
* }
* private void permute(int n) {
* if (n == 0) yield(a.clone());
* else for (int k = n; k >= 0; k--) {
* swap(n,k);
* permute(n - 1);
* swap(n,k);
* }
* }
* private void swap(int n, int m) {
* T temp = a[n];
* a[n] = a[m];
* a[m] = temp;
* }
* };
*}
*</pre>
*
* <b>NB:</b> this class makes use of Threads, you might want to double-check
* its source code before using it in a multi-threaded application.
*
* @author Adrian Kuhn <akuhn(at)iam.unibe.ch>
* @see http://smallwiki.unibe.ch/adriankuhn/yield4java/
*
*/
public abstract class Generator<T> implements Iterable<T> {
private class Iter implements Iterator<T>, Runnable {
private Object next = EMPTY;
public Iter() {
if (th != null) throw new IllegalStateException("Can not run coroutine twice");
th = new Thread(this);
th.setDaemon(true);
th.start();
}
@SuppressWarnings("deprecation")
@Override
protected void finalize() throws Throwable {
th.stop(); // let's commit suicide
}
public boolean hasNext() {
if (next == EMPTY) next = take();
return next != DONE;
}
@SuppressWarnings("unchecked")
public T next() {
if (next == EMPTY) next = take();
if (next == DONE) throw new NoSuchElementException();
Object temp = next;
next = EMPTY;
return (T) temp;
}
public void remove() {
throw new UnsupportedOperationException();
}
public void run() {
Generator.this.run();
done();
}
}
private static final Object DONE = new Object();
private static final Object EMPTY = new Object();
private Object drop = EMPTY;
private Thread th = null;
public synchronized void done() {
if (drop == DONE) throw new IllegalStateException();
if (drop != EMPTY) throw new IllegalStateException();
drop = DONE;
notifyAll();
}
public Iterator<T> iterator() {
return new Iter();
}
private synchronized void put(Object value) {
if (drop == DONE) throw new IllegalStateException();
if (drop != EMPTY) throw new IllegalStateException();
drop = value;
notifyAll();
while (drop != EMPTY) {
try {
wait();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
public abstract void run();
private synchronized Object take() {
while (drop == EMPTY) {
try {
wait();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
Object temp = drop;
if (drop != DONE) drop = EMPTY;
notifyAll();
return temp;
}
protected void yield(T value) {
put(value);
}
}