/* This file is part of Reactive Cascade which is released under The MIT License. See license.md , https://github.com/futurice/cascade and http://reactivecascade.com for details. This is open source for the common good. Please contribute improvements by pull request or contact paulirotta@gmail.com */ package com.reactivecascade.functional; import android.support.annotation.CheckResult; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.reactivecascade.i.IAction; import com.reactivecascade.i.IActionOne; import com.reactivecascade.i.IActionOneR; import com.reactivecascade.i.IActionR; import com.reactivecascade.i.IAltFuture; import com.reactivecascade.i.IReactiveTarget; import com.reactivecascade.i.ISettableAltFuture; import com.reactivecascade.i.IThreadType; import com.reactivecascade.util.AssertUtil; import com.reactivecascade.util.Origin; import com.reactivecascade.util.RCLog; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * A chain of two or more {@link com.reactivecascade.i.IAltFuture}s merged into a single logical entity. * <p> * This is useful for returning a single logical entity from an async method. It allows receiving values * at the head and output values to next chain steps at the tail. */ public class CompoundAltFuture<IN, OUT> extends Origin implements IAltFuture<IN, OUT> { protected final List<IAltFuture<?, ?>> mSubchain = new ArrayList<>(); @NonNull protected final IAltFuture<IN, ?> head; @NonNull protected final IAltFuture<?, OUT> tail; public CompoundAltFuture(@NonNull IAltFuture<IN, ?> head, @NonNull IAltFuture<?, OUT> tail) { AssertUtil.assertTrue("Head of CompoundAltFuture must not be downchain from an existing chain", head.getUpchain() == null); AssertUtil.assertNotEqual(head, tail); this.head = head; this.tail = tail; boolean foundHeadUpchainFromTail; IAltFuture<?, ?> previous = tail; do { mSubchain.add(0, previous); foundHeadUpchainFromTail = head.equals(previous); previous = previous.getUpchain(); } while (!foundHeadUpchainFromTail && previous != null); mSubchain.add(0, head); if (!foundHeadUpchainFromTail) { RCLog.throwIllegalArgumentException(head, "Head of CompoundAltFuture must be upchain from tail"); } } @NonNull @Override // IAltFuture public IThreadType getThreadType() { return head.getThreadType(); } @Override // IAltFuture public boolean isDone() { return tail.isDone(); } @Override // IAltFuture public boolean isForked() { return head.isForked(); } @Override // IAltFuture public boolean cancel(@NonNull String reason) { for (IAltFuture<?, ?> altFuture : mSubchain) { if (altFuture.cancel(reason)) { return true; } } return false; } @Override // IAltFuture public boolean cancel(@NonNull StateError stateError) { for (IAltFuture<?, ?> altFuture : mSubchain) { if (altFuture.cancel(stateError)) { RCLog.d(this, "Cancelled task within CompountAltFuture"); return true; } } return false; } @Override // IAltFuture public boolean isCancelled() { for (IAltFuture<?, ?> altFuture : mSubchain) { if (altFuture.isCancelled()) { RCLog.d(this, "CompountAltFuture is cancelled"); return true; } } return false; } @NonNull @Override // IAltFuture public IAltFuture<IN, OUT> fork() { head.fork(); return this; } @Nullable @Override // IAltFuture public IAltFuture<?, ? extends IN> getUpchain() { return head.getUpchain(); } @Override // IAltFuture public void setUpchain(@NonNull IAltFuture<?, ? extends IN> altFuture) { head.setUpchain(altFuture); } @Override // IAltFuture public void onError(@NonNull StateError stateError) throws Exception { head.onError(stateError); } @Override // IAltFuture public void onCancelled(@NonNull StateCancelled stateCancelled) throws Exception { head.onCancelled(stateCancelled); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) public IAltFuture<OUT, OUT> then(@NonNull IAction<OUT> action) { return tail.then(action); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) @SuppressWarnings("unchecked") public ISettableAltFuture<OUT> then(@NonNull IAction<? extends OUT>... actions) { return tail.then(actions); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) public IAltFuture<OUT, OUT> then(@NonNull IActionOne<OUT> action) { return tail.then(action); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) @SuppressWarnings("unchecked") public ISettableAltFuture<OUT> then(@NonNull IActionOne<OUT>... actions) { return tail.then(actions); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) public <DOWNCHAIN_OUT> IAltFuture<OUT, DOWNCHAIN_OUT> then(@NonNull IActionR<DOWNCHAIN_OUT> action) { return tail.then(action); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) public <DOWNCHAIN_OUT> IAltFuture<OUT, DOWNCHAIN_OUT> then(@NonNull IAltFuture<OUT, DOWNCHAIN_OUT> altFuture) { return tail.then(altFuture); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) public <DOWNCHAIN_OUT> IAltFuture<OUT, DOWNCHAIN_OUT> map(@NonNull IActionOneR<OUT, DOWNCHAIN_OUT> action) { return tail.map(action); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) @SuppressWarnings("unchecked") public <DOWNCHAIN_OUT> IAltFuture<OUT, DOWNCHAIN_OUT>[] map(@NonNull IActionOneR<OUT, DOWNCHAIN_OUT>... actions) { return tail.map(actions); } /** * Pause execution of this chain for a fixed time interval * <p> * Note that the chain realizes immediately in the event of {@link #cancel(String)} or a runtime error * * @param sleepTime * @param timeUnit * @return */ @NonNull @Override public ISettableAltFuture<OUT> sleep(long sleepTime, @NonNull final TimeUnit timeUnit) { throw new UnsupportedOperationException("Not yet implemented"); //TODO sleep a compound alt future } // @NonNull // @Override // IAltFuture // @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) // public ISettableAltFuture<OUT> await(@NonNull IAltFuture<?, ?> altFuture) { // return tail.await(altFuture); // } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) @SuppressWarnings("unchecked") public ISettableAltFuture<OUT> await(@NonNull IAltFuture<?, ?>... altFuturesToJoin) { return tail.await(altFuturesToJoin); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) public IAltFuture<?, OUT> on(@NonNull IThreadType theadType) { if (theadType == tail.getThreadType()) { return this; } return tail.on(theadType); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) public IAltFuture<IN, IN> filter(@NonNull IActionOneR<IN, Boolean> action) { return head.filter(action); } @NonNull @Override // IAltFuture @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) public IAltFuture<OUT, OUT> set(@NonNull IReactiveTarget<OUT> reactiveTarget) { return tail.set(reactiveTarget); } @NonNull @Override // IAltFuture public ISettableAltFuture<OUT> onError(@NonNull IActionOne<Exception> onErrorAction) { return tail.onError(onErrorAction); } @NonNull @Override // IAltFuture public ISettableAltFuture<OUT> onCancelled(@NonNull IActionOne<String> onCancelledAction) { return tail.onCancelled(onCancelledAction); } @NonNull @Override // IAltFuture public OUT safeGet() { return tail.safeGet(); } @NonNull @Override // IAltFuture public OUT get() { return tail.get(); } }