package com.project.shared.data.funcs; import com.project.shared.data.Pair; import com.project.shared.data.funcs.Func.Action; import com.project.shared.utils.ThrowableUtils; import com.project.shared.utils.loggers.Logger; public abstract class AsyncFunc<A, B> { protected abstract <S, E> void run(A arg, Func<B, S> successHandler, Func<Throwable, E> errorHandler); public void run(A arg) { //Logger.log("Starting AsyncFunc with arg: " + arg); this.run(arg, Action.<B>empty(), Action.<Throwable>empty()); } public <C> AsyncFunc<A, C> then(final AsyncFunc<B, C> success) { return this.then(success, null); } public <C> AsyncFunc<A, C> then(Func<B, C> success) { return this.then(success, null); } public <C> AsyncFunc<A, C> then(Func<B, C> success, Func<Throwable, C> error) { return this.then(null == success ? null : AsyncFunc.fromFunc(success), null == error ? null : AsyncFunc.fromFunc(error)); } public <C> AsyncFunc<A, C> then(final AsyncFunc<B, C> success, final AsyncFunc<Throwable, C> recover) { return this.thenSelect(AsyncFunc.<B,AsyncFunc<B,C>>constFunc(success), recover); } public <C> AsyncFunc<A, C> thenSelect(final AsyncFunc<B, AsyncFunc<B, C>> successSelector, final AsyncFunc<Throwable, C> recover) { return AsyncFunc.<A,B,C>chain(this, successSelector, recover); } public <C> AsyncFunc<A, C> thenSelect(final AsyncFunc<B, AsyncFunc<B,C>> successSelector) { return this.thenSelect(successSelector, null); } public <C> AsyncFunc<A, C> thenSelect(final Func<B, AsyncFunc<B,C>> successSelector) { return this.thenSelect(AsyncFunc.fromFunc(successSelector), null); } public AsyncFunc<A, Pair<B,B>> and(final AsyncFunc<A,B> other) { return AsyncFunc.<A,B>both(this, other); } /** * Converts this AsyncFunc to one with any result type. The result value will always be the value given 'res'. */ public <C> AsyncFunc<A,C> constResult(C res) { return this.then(AsyncFunc.<B,C>constFunc(res)); } public <C> AsyncFunc<C,B> constArg(final A constArg) { return AsyncFunc.<C,A>constFunc(constArg).then(this); } public static <A,B> AsyncFunc<A, B> constFunc(final B value) { return new AsyncFunc<A,B>(){ @Override protected <S, E> void run(A arg, final Func<B, S> successHandler, Func<Throwable, E> errorHandler) { successHandler.apply(value); }}; } private static <A,B,C> AsyncFunc<A, C> chain(final AsyncFunc<A, B> first, final AsyncFunc<B, AsyncFunc<B, C>> secondSelector, final AsyncFunc<Throwable, C> recover) { return new AsyncFunc<A,C>(){ @Override protected <S, E> void run(A arg, final Func<C, S> successHandler, final Func<Throwable, E> errorHandler) { //Logger.log("Starting chained ('then') AsyncFunc with arg: " + arg); first.run(arg, new Action<B>() { @Override public void exec(final B arg) { //Logger.log("Chained AsyncFunc succeeded, next in chain with arg: " + arg); secondSelector.run(arg, new Func<AsyncFunc<B,C>,S>(){ @Override public S apply(final AsyncFunc<B, C> success) { //Logger.log("Chained AsyncFunc succeeded, next in chain with arg: " + arg); if (null != success) { success.run(arg, successHandler, errorHandler); } return null; }}, new Action<Throwable> () { @Override public void exec(Throwable arg) { //Logger.log("Chained AsyncFunc failed, next in chain with arg: " + arg); if (null != recover) { recover.run(arg, successHandler, errorHandler); } }}); } }, new Action<Throwable>() { @Override public void exec(Throwable arg) { //Logger.log("Chained AsyncFunc failed, next in chain with arg: " + arg); if (null != recover) { recover.run(arg, successHandler, errorHandler); } } }); } }; } private static <A,B> AsyncFunc<A, Pair<B,B>> both(final AsyncFunc<A, B> left, final AsyncFunc<A, B> right) { return new AsyncFunc<A, Pair<B,B>>() { @Override protected <S, E> void run(A arg, final Func<Pair<B, B>, S> successHandler, Func<Throwable, E> errorHandler) { final int[] numCompleted = {0}; Func<B, S> singleSuccessHandler = new Func<B, S>(){ private B _firstResult; @Override public S apply(B arg) { numCompleted[0] += 1; if (1 == numCompleted[0]) { this._firstResult = arg; return null; } // Both completed return successHandler.apply(new Pair<B, B>(this._firstResult, arg)); }}; left.run(arg, singleSuccessHandler, Action.<Throwable>empty()); right.run(arg, singleSuccessHandler, Action.<Throwable>empty()); } }; } public static <A,B> AsyncFunc<A, B> fromFunc(final Func<A,B> func) { return new AsyncFunc<A,B>() { @Override protected <S,E> void run(A arg, Func<B, S> successHandler, Func<Throwable, E> errorHandler) { B res = null; try { res = func.apply(arg); } catch (Throwable e) { // TODO perhaps use the errorHandler here? Logger.info(this, "Error in AsyncFunc: " + e.toString()); Logger.info(this, ThrowableUtils.joinStackTrace(e)); throw new RuntimeException(e); } successHandler.apply(res); } }; } public static <A> AsyncFunc<A, A> immediate() { return new AsyncFunc<A, A>() { @Override protected <S, E> void run(A arg, Func<A, S> successHandler, Func<Throwable, E> errorHandler) { successHandler.apply(arg); } @Override public <C> AsyncFunc<A, C> then(final AsyncFunc<A, C> success, final AsyncFunc<Throwable, C> recover) { return success; } }; } };