![JAR search and dependency download from the Maven repository](/logo.png)
mutiny.zero.operators.Concatenate Maven / Gradle / Ivy
Show all versions of mutiny-zero Show documentation
package mutiny.zero.operators;
import static java.util.Objects.requireNonNull;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Flow;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import mutiny.zero.internal.Helper;
/**
* A {@link java.util.concurrent.Flow.Publisher} that is the concatenation of several ones.
*
* The first publisher in the list is the first to be subscribed.
* Once it completes the next one is subscribed and so on, up to the completion of the last one.
*
* If any publisher sends an error signal then the concatenation stream ends with that error.
*
* @param the elements type
*/
public class Concatenate implements Flow.Publisher {
private final List> publishers;
/**
* Create a new concatenation publisher.
*
* @param publishers the list of publishers, must not be {@code null}, must not contain {@code null}
*/
public Concatenate(List> publishers) {
this.publishers = requireNonNull(publishers, "The publishers list cannot be null");
for (Flow.Publisher publisher : publishers) {
requireNonNull(publisher, "A publisher cannot be null");
}
}
@Override
public void subscribe(Flow.Subscriber super T> subscriber) {
requireNonNull(subscriber, "The subscriber cannot be null");
Processor processor = new Processor();
processor.subscribe(subscriber);
}
private class Processor implements Flow.Processor, Flow.Subscription {
private Flow.Subscriber super T> downstream;
private Flow.Subscription upstreamSubscription;
private final AtomicBoolean cancelled = new AtomicBoolean();
private final AtomicLong demand = new AtomicLong();
private volatile boolean unboundedDemand;
private boolean downstreamIsReady;
private final Iterator> publisherIterator = publishers.iterator();
@Override
public void subscribe(Flow.Subscriber super T> subscriber) {
downstream = subscriber;
subscribeNext();
}
private void subscribeNext() {
if (publisherIterator.hasNext()) {
Flow.Publisher publisher = publisherIterator.next();
publisher.subscribe(this);
} else {
downstream.onComplete();
}
}
@Override
public void onSubscribe(Flow.Subscription subscription) {
if (cancelled.get()) {
return;
}
this.upstreamSubscription = subscription;
if (downstreamIsReady) {
long n = demand.get();
if (n > 0L) {
this.upstreamSubscription.request(n);
}
} else {
downstreamIsReady = true;
downstream.onSubscribe(this);
}
}
@Override
public void onNext(T item) {
if (!cancelled.get()) {
if (!unboundedDemand) {
demand.decrementAndGet();
}
downstream.onNext(item);
}
}
@Override
public void onError(Throwable throwable) {
if (!cancelled.get()) {
cancel();
downstream.onError(throwable);
}
}
@Override
public void onComplete() {
if (!cancelled.get()) {
subscribeNext();
}
}
@Override
public void request(long n) {
if (cancelled.get()) {
return;
}
if (n <= 0L) {
onError(Helper.negativeRequest(n));
} else {
Helper.add(demand, n);
if (n == Long.MAX_VALUE) {
unboundedDemand = true;
}
upstreamSubscription.request(n);
}
}
@Override
public void cancel() {
if (cancelled.compareAndSet(false, true)) {
upstreamSubscription.cancel();
upstreamSubscription = null;
}
}
}
}