package com.koushikdutta.async.future; import com.koushikdutta.async.callback.CompletedCallback; import com.koushikdutta.async.callback.ContinuationCallback; import java.util.LinkedList; public class Continuation extends SimpleCancellable implements ContinuationCallback, Runnable, Cancellable { CompletedCallback callback; Runnable cancelCallback; public CompletedCallback getCallback() { return callback; } public void setCallback(CompletedCallback callback) { this.callback = callback; } public Runnable getCancelCallback() { return cancelCallback; } public void setCancelCallback(Runnable cancelCallback) { this.cancelCallback = cancelCallback; } public void setCancelCallback(final Cancellable cancel) { if (cancel == null) { this.cancelCallback = null; return; } this.cancelCallback = new Runnable() { @Override public void run() { cancel.cancel(); } }; } public Continuation() { this(null); } public Continuation(CompletedCallback callback) { this(callback, null); } public Continuation(CompletedCallback callback, Runnable cancelCallback) { this.cancelCallback = cancelCallback; this.callback = callback; } private CompletedCallback wrap() { return new CompletedCallback() { boolean mThisCompleted; @Override public void onCompleted(Exception ex) { // onCompleted may be called more than once... buggy code. // only accept the first (timeouts, etc) if (mThisCompleted) return; mThisCompleted = true; assert waiting; waiting = false; if (ex == null) { next(); return; } reportCompleted(ex); } }; } void reportCompleted(Exception ex) { if (!setComplete()) return; if (callback != null) callback.onCompleted(ex); } LinkedList<ContinuationCallback> mCallbacks = new LinkedList<ContinuationCallback>(); private ContinuationCallback hook(ContinuationCallback callback) { if (callback instanceof DependentCancellable) { DependentCancellable child = (DependentCancellable)callback; child.setParent(this); } return callback; } public Continuation add(ContinuationCallback callback) { mCallbacks.add(hook(callback)); return this; } public Continuation insert(ContinuationCallback callback) { mCallbacks.add(0, hook(callback)); return this; } public void add(final DependentFuture future) { future.setParent(this); add(new ContinuationCallback() { @Override public void onContinue(Continuation continuation, CompletedCallback next) throws Exception { future.get(); next.onCompleted(null); } }); } private boolean inNext; private boolean waiting; private void next() { if (inNext) return; while (mCallbacks.size() > 0 && !waiting && !isDone() && !isCancelled()) { ContinuationCallback cb = mCallbacks.remove(); try { inNext = true; waiting = true; cb.onContinue(this, wrap()); } catch (Exception e) { reportCompleted(e); } finally { inNext = false; } } if (waiting) return; if (isDone()) return; if (isCancelled()) return; reportCompleted(null); } @Override public boolean cancel() { if (!super.cancel()) return false; if (cancelCallback != null) cancelCallback.run(); return true; } boolean started; public Continuation start() { if (started) throw new IllegalStateException("already started"); started = true; next(); return this; } @Override public void onContinue(Continuation continuation, CompletedCallback next) throws Exception { setCallback(next); start(); } @Override public void run() { start(); } }