package org.tessell.model.commands;
import org.tessell.dispatch.client.events.DispatchUnhandledFailureEvent;
import org.tessell.dispatch.client.util.OutstandingDispatchAsync;
import org.tessell.dispatch.shared.Action;
import org.tessell.dispatch.shared.Result;
import com.google.gwt.user.client.rpc.AsyncCallback;
/**
* Maintains the state of a {@code gwt-dispatch}-style UiCommand.
*
* Along with {@link UiCommand}'s {@code enabled} property, this adds an {@code active}
* property that denotes whether a dispatch command has already fired an action and is
* waiting for a result.
*
* This allows conditional action on the command's activeness, e.g. disabling buttons.
*/
public abstract class DispatchUiCommand<A extends Action<R>, R extends Result> extends AsyncUiCommand {
private final OutstandingDispatchAsync async;
private int highestActionIndex;
private int highestResultIndex;
private int currentActionIndex;
protected R result;
public DispatchUiCommand(OutstandingDispatchAsync async) {
this.async = async;
}
@Override
protected final void doExecute() {
doExecute(createAction());
}
protected final void doExecute(final A action) {
if (action != null) {
active.set(true);
final int thisActionIndex = ++highestActionIndex;
// It would be nice to use a SuccessCallback, but we need to know
// when the failure happened to toggle active back to false
async.execute(action, new AsyncCallback<R>() {
public void onSuccess(R r) {
highestResultIndex = Math.max(highestResultIndex, thisActionIndex);
currentActionIndex = thisActionIndex;
result = r;
if (thisActionIndex == highestActionIndex) {
active.set(false);
}
onResult();
}
public void onFailure(Throwable caught) {
highestResultIndex = Math.max(highestResultIndex, thisActionIndex);
currentActionIndex = thisActionIndex;
result = null;
DispatchUiCommand.this.onFailure(caught);
if (thisActionIndex == highestActionIndex) {
active.set(false);
}
}
});
}
}
/** Implemented by subclasses to create the action object. */
protected abstract A createAction();
/** Implemented by subclasses to handle a successful result. */
protected abstract void onResult();
/** Fires a {@link DispatchUnhandledFailureEvent} on failures, can be overridden by subclasses if needed. */
protected void onFailure(Throwable caught) {
async.unhandledFailure(caught);
}
/** Allows subclasses to tell if the current action is stale. */
protected boolean hasNewerActionBeenSent() {
return currentActionIndex < highestActionIndex;
}
/** Allows subclasses to tell if the current result is stale. */
protected boolean hasNewerResultBeenReceived() {
return currentActionIndex < highestResultIndex;
}
}