All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hu.akarnokd.asyncenum.AsyncTakeUntil Maven / Gradle / Ivy

/*
 * Copyright 2017 David Karnok
 *
 * 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 hu.akarnokd.asyncenum;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;

final class AsyncTakeUntil implements AsyncEnumerable {

    final AsyncEnumerable source;

    final AsyncEnumerable other;

    AsyncTakeUntil(AsyncEnumerable source, AsyncEnumerable other) {
        this.source = source;
        this.other = other;
    }

    @Override
    public AsyncEnumerator enumerator() {
        AsyncEnumerator otherEnum = other.enumerator();
        TakeUntilEnumerator main = new TakeUntilEnumerator<>(otherEnum);
        otherEnum.moveNext().whenComplete(main::acceptOther);
        AsyncEnumeratorHelper.replace(main.source, source.enumerator());
        return main;
    }

    static final class TakeUntilEnumerator
            extends AtomicReference>
            implements AsyncEnumerator,
            BiConsumer {

        final AsyncEnumerator other;

        final AtomicReference> source;

        CompletableFuture current;

        TakeUntilEnumerator(AsyncEnumerator other) {
            this.other = other;
            this.source = new AtomicReference<>();
        }

        @Override
        public CompletionStage moveNext() {
            for (;;) {
                CompletableFuture curr = get();
                if (curr instanceof TerminalCompletableFuture) {
                    other.cancel();
                    return curr;
                }
                CompletableFuture next = new CompletableFuture<>();
                AsyncEnumerator en = source.getAcquire();
                if (compareAndSet(curr, next)) {
                    current = next;
                    en.moveNext().whenComplete(this);
                    return next;
                }
            }
        }

        @Override
        public T current() {
            return source.getPlain().current();
        }


        @Override
        public void accept(Boolean aBoolean, Throwable throwable) {
            if (throwable != null) {
                other.cancel();
                current.completeExceptionally(throwable);
                return;
            }
            if (!aBoolean) {
                other.cancel();
            }
            current.complete(aBoolean);
        }

        public void acceptOther(Boolean aBoolean, Throwable throwable) {
            if (throwable == null) {
                CompletableFuture cf = getAndSet(STOP);
                AsyncEnumeratorHelper.cancel(source);
                if (cf != null && !(cf instanceof TerminalCompletableFuture)) {
                    cf.complete(false);
                }
            } else {
                TerminalCompletableFuture tf = new TerminalCompletableFuture();
                tf.completeExceptionally(throwable);
                CompletableFuture cf = getAndSet(tf);
                AsyncEnumeratorHelper.cancel(source);
                if (cf != null && !(cf instanceof TerminalCompletableFuture)) {
                    cf.completeExceptionally(throwable);
                }
            }
        }

        @Override
        public void cancel() {
            other.cancel();
            AsyncEnumeratorHelper.cancel(source);
        }

        static final TerminalCompletableFuture STOP = new TerminalCompletableFuture();
        static {
            STOP.complete(false);
        }

        static final class TerminalCompletableFuture extends CompletableFuture {

        }
    }
}