
com.github.davidmoten.rx.internal.operators.TransformerStateMachine Maven / Gradle / Ivy
package com.github.davidmoten.rx.internal.operators;
import com.github.davidmoten.rx.util.BackpressureStrategy;
import com.github.davidmoten.util.Preconditions;
import rx.Notification;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Observable.Transformer;
import rx.Subscriber;
import rx.functions.Func0;
import rx.functions.Func1;
import rx.functions.Func2;
import rx.functions.Func3;
public final class TransformerStateMachine implements Transformer {
private final Func0 extends State> initialState;
private final Func3 super State, ? super In, ? super Subscriber, ? extends State> transition;
private final Func2 super State, ? super Subscriber, Boolean> completion;
private final BackpressureStrategy backpressureStrategy;
private final int initialRequest;
private TransformerStateMachine(Func0 extends State> initialState,
Func3 super State, ? super In, ? super Subscriber, ? extends State> transition,
Func2 super State, ? super Subscriber, Boolean> completion,
BackpressureStrategy backpressureStrategy, int initialRequest) {
Preconditions.checkNotNull(initialState);
Preconditions.checkNotNull(transition);
Preconditions.checkNotNull(completion);
Preconditions.checkNotNull(backpressureStrategy);
Preconditions.checkArgument(initialRequest > 0, "initialRequest must be greater than zero");
this.initialState = initialState;
this.transition = transition;
this.completion = completion;
this.backpressureStrategy = backpressureStrategy;
this.initialRequest = initialRequest;
}
public static Transformer create(Func0 extends State> initialState,
Func3 super State, ? super In, ? super Subscriber, ? extends State> transition,
Func2 super State, ? super Subscriber, Boolean> completion,
BackpressureStrategy backpressureStrategy, int initialRequest) {
return new TransformerStateMachine(initialState, transition, completion,
backpressureStrategy, initialRequest);
}
@Override
public Observable call(final Observable source) {
// use defer so we can have a single state reference for each
// subscription
return Observable.defer(new Func0>() {
@Override
public Observable call() {
Mutable state = new Mutable(initialState.call());
return source.materialize()
// do state transitions and emit notifications
// use flatMap to emit notification values
.flatMap(execute(transition, completion, state, backpressureStrategy),
initialRequest)
// complete if we encounter an unsubscribed sentinel
.takeWhile(NOT_UNSUBSCRIBED)
// flatten notifications to a stream which will enable
// early termination from the state machine if desired
.dematerialize();
}
});
}
private static Func1, Observable>> execute(
final Func3 super State, ? super In, ? super Subscriber, ? extends State> transition,
final Func2 super State, ? super Subscriber, Boolean> completion,
final Mutable state, final BackpressureStrategy backpressureStrategy) {
return new Func1, Observable>>() {
@Override
public Observable> call(final Notification in) {
Observable> o = Observable
.create(new OnSubscribe>() {
@Override
public void call(Subscriber super Notification> subscriber) {
Subscriber w = wrap(subscriber);
if (in.hasValue()) {
state.value = transition.call(state.value, in.getValue(), w);
if (!subscriber.isUnsubscribed())
subscriber.onCompleted();
else {
// this is a special emission to indicate that the
// transition called unsubscribe. It will be
// filtered later.
subscriber.onNext(UnsubscribedNotificationHolder.unsubscribedNotification());
}
} else if (in.isOnCompleted()) {
if (completion.call(state.value, w) && !subscriber.isUnsubscribed()) {
w.onCompleted();
}
} else if (!subscriber.isUnsubscribed()) {
w.onError(in.getThrowable());
}
}
});
// because the observable we just created does not
// support backpressure we need to apply a backpressure
// handling operator. This operator is supplied by the
// user.
return applyBackpressure(o, backpressureStrategy);
}
};
}
private static final class UnsubscribedNotificationHolder {
private static final Notification
© 2015 - 2025 Weber Informatics LLC | Privacy Policy