org.aksw.commons.rx.op.FlowableOperatorConditionalConcat Maven / Gradle / Ivy
package org.aksw.commons.rx.op;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import org.aksw.commons.collector.core.Accumulators;
import org.aksw.commons.collector.domain.Accumulator;
import org.aksw.commons.collector.domain.Aggregator;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.FlowableOperator;
import io.reactivex.rxjava3.core.FlowableSubscriber;
import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper;
import io.reactivex.rxjava3.internal.util.BackpressureHelper;
/**
* Track all seen items of the downstream flowable. When the downstream completes,
* invoke an action that creates a new publisher based on the seen items that
* will be concatenated.
*
*
* {@code
* Flowable>> list = Flowable
* .range(0, 10)
* .map(i -> Maps.immutableEntry((int)(i / 3), i))
* .lift(FlowableOperatorSequentialGroupBy., Integer, List>create(Entry::getKey, ArrayList::new, (acc, e) -> acc.add(e.getValue())));
*
* }
*
* @author raven
*
* @param Item type
* @param Group key type
* @param Accumulator type
*/
public final class FlowableOperatorConditionalConcat
implements FlowableOperator {
/** The amount of items to read ahead */
protected Aggregator aggregator;
protected Function super C, Flowable> tailFlowFactory;
public FlowableOperatorConditionalConcat(Aggregator aggregator, Function super C, Flowable> tailFlowFactory) {
super();
this.aggregator = aggregator;
this.tailFlowFactory = tailFlowFactory;
}
/**
* Create method with the following characteristics:
*
* - the accumulator constructor is a mere supplier (and thus neither depends on the accumulator count nor the group Key)
* - Group keys are compared using Objects::equals
*
*/
public static FlowableOperatorConditionalConcat create(
Aggregator aggregator,
Function> tailFlowFactory) {
return new FlowableOperatorConditionalConcat(aggregator, tailFlowFactory);
}
@Override
public Subscriber super T> apply(Subscriber super T> downstream) throws Exception {
return new SubscriberImpl(downstream);
}
/** This subscriber first consumes the initial upstream and caches all seen items.
* Afterwards, a new flowable is created from those items and the subscriber attaches itself
* to that new flowable. */
public class SubscriberImpl
implements FlowableSubscriber, Subscription
{
protected Subscriber super T> downstream;
protected Subscription upstream;
protected boolean isInitialUpstreamComplete = false;
protected Accumulator accumulator;
protected AtomicLong downstreamDemand;
public SubscriberImpl(Subscriber super T> downstream) {
this.downstream = downstream;
this.accumulator = Accumulators.synchronize(aggregator.createAccumulator());
this.downstreamDemand = new AtomicLong();
}
@Override
public void onSubscribe(Subscription s) {
if (upstream != null) {
s.cancel();
} else {
upstream = s;
// Only subscribe to the downstream once
if (!isInitialUpstreamComplete) {
downstream.onSubscribe(this);
}
long remainingDemand = downstreamDemand.get();
if (remainingDemand != 0) {
upstream.request(remainingDemand);
}
}
}
@Override
public void onNext(T item) {
accumulator.accumulate(item);
downstream.onNext(item);
downstreamDemand.decrementAndGet();
}
/** Called when the upstream completes */
@Override
public void onComplete() {
if (!isInitialUpstreamComplete) {
isInitialUpstreamComplete = true;
upstream = null;
C accumulatedValue = accumulator.getValue();
Flowable tailFlow = tailFlowFactory.apply(accumulatedValue);
if (tailFlow != null) {
tailFlow.subscribe(this);
}
} else {
downstream.onComplete();
}
}
@Override
public void onError(Throwable t) {
downstream.onError(t);
}
@Override
public void request(long n) {
if (SubscriptionHelper.validate(n)) {
BackpressureHelper.add(downstreamDemand, n);
}
}
@Override
public void cancel() {
upstream.cancel();
upstream = SubscriptionHelper.CANCELLED;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy