//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // This file is a part of the 'coroutines' project. // Copyright 2018 Elmar Sonnenschein, esoco GmbH, Flensburg, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ package de.esoco.coroutine.step; import de.esoco.coroutine.Continuation; import de.esoco.coroutine.Coroutine; import de.esoco.coroutine.CoroutineStep; import java.util.concurrent.CompletableFuture; import java.util.function.BiPredicate; import java.util.function.Predicate; import static de.esoco.coroutine.step.CodeExecution.consume; /******************************************************************** * A {@link Coroutine} step that loops over another step (which may be a * subroutine) as long as a predicate yields TRUE for the input value and/or the * current continuation. The looped step get's the loop input value as it's * input on the first iteration and must return a value of the same type which * will then be used to test the condition before the next loop run. * * <p>If more complex conditions need to be checked the loop condition can check * relations that have been set in the {@link Continuation} by the looped * step.</p> * * @author eso */ public class Loop<T> extends CoroutineStep<T, T> { //~ Instance fields -------------------------------------------------------- private final BiPredicate<? super T, Continuation<?>> pCondition; private final CoroutineStep<T, T> rLoopedStep; //~ Constructors ----------------------------------------------------------- /*************************************** * Creates a new instance. * * @param fCondition The condition to check for TRUE to continue looping * @param rLoopedStep The step to execute in the loop */ public Loop( BiPredicate<? super T, Continuation<?>> fCondition, CoroutineStep<T, T> rLoopedStep) { this.pCondition = fCondition; this.rLoopedStep = rLoopedStep; } //~ Static methods --------------------------------------------------------- /*************************************** * Repeatedly executes a certain step as long as the given condition is TRUE * for the current value and the continuation of the execution. The current * value is initialized from the loop input value and updated by the looped * step on each iteration. * * @param pCondition The condition to check for TRUE to continue looping * @param rLoopedStep The step to execute in the loop * * @return A new step instance */ public static <T> Loop<T> loopWhile( BiPredicate<? super T, Continuation<?>> pCondition, CoroutineStep<T, T> rLoopedStep) { return new Loop<>(pCondition, rLoopedStep); } /*************************************** * Repeatedly executes a certain step as long as the given condition is TRUE * for the current value. The current value is initialized from the loop * input value and updated by the looped step on each iteration. * * <p>If more complex conditions need to be checked the method {@link * #loopWhile(BiPredicate, CoroutineStep)} can be used to check relations * that have been set in the {@link Continuation} by the looped step.</p> * * @param pCondition The condition to check for TRUE to continue looping * @param rLoopedStep The step to execute in the loop * * @return A new step instance */ public static <T> Loop<T> loopWhile( Predicate<T> pCondition, CoroutineStep<T, T> rLoopedStep) { return loopWhile((i, c) -> pCondition.test(i), rLoopedStep); } //~ Methods ---------------------------------------------------------------- /*************************************** * {@inheritDoc} */ @Override public void runAsync(CompletableFuture<T> fPreviousExecution, CoroutineStep<T, ?> rNextStep, Continuation<?> rContinuation) { fPreviousExecution.thenAcceptAsync( i -> loopAsync(i, rNextStep, rContinuation)); } /*************************************** * {@inheritDoc} */ @Override protected T execute(T rValue, Continuation<?> rContinuation) { while (pCondition.test(rValue, rContinuation)) { rValue = rLoopedStep.runBlocking(rValue, rContinuation); } return rValue; } /*************************************** * Performs the actual looping by repeatedly invoking this method until the * loop condition is FALSE. * * @param rInput The input value to the loop * @param rNextStep The next step to be invoked when the condition is * FALSE * @param rContinuation The continuation of the execution */ private void loopAsync(T rInput, CoroutineStep<T, ?> rNextStep, Continuation<?> rContinuation) { if (pCondition.test(rInput, rContinuation)) { CompletableFuture<T> fRunLoop = CompletableFuture.supplyAsync(() -> rInput, rContinuation); rLoopedStep.runAsync( fRunLoop, consume(i -> loopAsync(i, rNextStep, rContinuation)), rContinuation); } else { rContinuation.suspend(this, rNextStep).resume(rInput); } } }