package org.tessell.util; import java.util.ArrayList; import java.util.HashMap; import org.tessell.dispatch.client.SuccessCallback; import org.tessell.dispatch.client.util.OutstandingDispatchAsync; import org.tessell.dispatch.shared.Action; import org.tessell.dispatch.shared.Result; /** * @param A * the action * @param R * the result * @param D * your derived result */ public abstract class ActionCache<A extends Action<R>, R extends Result, D> { private final OutstandingDispatchAsync async; private final HashMap<A, ResultHandler> results = new HashMap<A, ResultHandler>(); public ActionCache(final OutstandingDispatchAsync async) { this.async = async; } public void execute(final A action) { execute(action, null); } public abstract D derive(R result); /** Executes <code>A</code> once, queueing any <code>onSuccess</code> until it arrives. */ public void execute(final A action, final SuccessCallback<D> onSuccess) { ResultHandler h = results.get(action); if (h == null) { h = new ResultHandler(action); results.put(action, h); } h.execute(onSuccess); } /** Clears all cached results. */ public void reset() { results.clear(); } /** For given action, calls and caches its derived result. */ private class ResultHandler implements SuccessCallback<R> { private final A action; private final ArrayList<SuccessCallback<D>> onSuccess = new ArrayList<SuccessCallback<D>>(); private D derived; private boolean calling; private ResultHandler(final A action) { this.action = action; } private void execute(final SuccessCallback<D> onSuccess) { if (derived != null) { if (onSuccess != null) { onSuccess.onSuccess(derived); } } else if (calling) { if (onSuccess != null) { this.onSuccess.add(onSuccess); } } else { if (onSuccess != null) { this.onSuccess.add(onSuccess); } calling = true; async.execute(action, this); } } public void onSuccess(final R result) { derived = derive(result); for (final SuccessCallback<D> s : onSuccess) { s.onSuccess(derived); } onSuccess.clear(); } } }