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

com.github.davidmoten.rx.Transformers Maven / Gradle / Ivy

package com.github.davidmoten.rx;

import java.nio.charset.CharsetDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;

import com.github.davidmoten.rx.StateMachine.Completion;
import com.github.davidmoten.rx.StateMachine.Transition;
import com.github.davidmoten.rx.buffertofile.DataSerializer;
import com.github.davidmoten.rx.buffertofile.DataSerializers;
import com.github.davidmoten.rx.buffertofile.Options;
import com.github.davidmoten.rx.internal.operators.OnSubscribeDoOnEmpty;
import com.github.davidmoten.rx.internal.operators.OperatorBufferPredicateBoundary;
import com.github.davidmoten.rx.internal.operators.OperatorBufferToFile;
import com.github.davidmoten.rx.internal.operators.OperatorDoOnNth;
import com.github.davidmoten.rx.internal.operators.OperatorFromTransformer;
import com.github.davidmoten.rx.internal.operators.TransformerOnTerminateResume;
import com.github.davidmoten.rx.internal.operators.OperatorSampleFirst;
import com.github.davidmoten.rx.internal.operators.OperatorWindowMinMax;
import com.github.davidmoten.rx.internal.operators.OperatorWindowMinMax.Metric;
import com.github.davidmoten.rx.internal.operators.OrderedMerge;
import com.github.davidmoten.rx.internal.operators.TransformerDecode;
import com.github.davidmoten.rx.internal.operators.TransformerDelayFinalUnsubscribe;
import com.github.davidmoten.rx.internal.operators.TransformerLimitSubscribers;
import com.github.davidmoten.rx.internal.operators.TransformerOnBackpressureBufferRequestLimiting;
import com.github.davidmoten.rx.internal.operators.TransformerStateMachine;
import com.github.davidmoten.rx.internal.operators.TransformerStringSplit;
import com.github.davidmoten.rx.util.BackpressureStrategy;
import com.github.davidmoten.rx.util.MapWithIndex;
import com.github.davidmoten.rx.util.MapWithIndex.Indexed;
import com.github.davidmoten.rx.util.Pair;
import com.github.davidmoten.util.Optional;

import rx.Notification;
import rx.Observable;
import rx.Observable.Operator;
import rx.Observable.Transformer;
import rx.Observer;
import rx.Scheduler;
import rx.Scheduler.Worker;
import rx.Subscriber;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Action2;
import rx.functions.Func0;
import rx.functions.Func1;
import rx.functions.Func2;
import rx.functions.Func3;
import rx.internal.util.RxRingBuffer;
import rx.observables.GroupedObservable;
import rx.schedulers.Schedulers;

public final class Transformers {

    static final int DEFAULT_INITIAL_BATCH = 1;

    public static  Operator toOperator(
            Func1, ? extends Observable> function) {
        return OperatorFromTransformer.toOperator(function);
    }

    public static  Transformer collectStats() {
        return new Transformer() {

            @Override
            public Observable call(Observable o) {
                return o.scan(Statistics.create(), Functions.collectStats());
            }
        };
    }

    public static  Transformer> collectStats(
            final Func1 function) {
        return new Transformer>() {

            @Override
            public Observable> call(Observable source) {
                return source.scan(Pair.create((T) null, Statistics.create()),
                        new Func2, T, Pair>() {
                            @Override
                            public Pair call(Pair pair, T t) {
                                return Pair.create(t, pair.b().add(function.call(t)));
                            }
                        }).skip(1);
            }
        };
    }

    public static > Transformer sort() {
        return new Transformer() {

            @Override
            public Observable call(Observable o) {
                return o.toSortedList().flatMapIterable(Functions.> identity());
            }
        };
    }

    public static  Transformer sort(final Comparator comparator) {
        return new Transformer() {

            @Override
            public Observable call(Observable o) {
                return o.toSortedList(Functions.toFunc2(comparator))
                        .flatMapIterable(Functions.> identity());
            }
        };
    }

    public static  Transformer> toSet() {
        return new Transformer>() {

            @Override
            public Observable> call(Observable o) {
                return o.collect(new Func0>() {

                    @Override
                    public Set call() {
                        return new HashSet();
                    }
                }, new Action2, T>() {

                    @Override
                    public void call(Set set, T t) {
                        set.add(t);
                    }
                });
            }
        };
    }

    /**
     * 

* Returns a {@link Transformer} that wraps stream emissions with their * corresponding zero based index numbers (0,1,2,3,..) in instances of * {@link Indexed}. *

* Example usage: * *

     * 
     *  {@code
     *    Observable
     *      .just("a","b","c)
     *      .mapWithIndex(Transformers.mapWithIndex())
     *      .map(x -> x.index() + "->" + x.value())
     *      .forEach(System.out::println);
     *  }
     * 
* *

* marble diagram * * @param * generic type of the stream being supplemented with an index * @return transformer that supplements each stream emission with its index * (zero-based position) in the stream. */ public static Transformer> mapWithIndex() { return MapWithIndex.instance(); } /** *

* Returns a {@link Transformer} that allows processing of the source stream * to be defined in a state machine where transitions of the state machine * may also emit items to downstream that are buffered if necessary when * backpressure is requested. flatMap is part of the processing * chain so the source may experience requests for more items than are * strictly required by the endpoint subscriber. * *

* marble diagram * * @param initialStateFactory * the factory to create the initial state of the state machine. * @param transition * defines state transitions and consequent emissions to * downstream when an item arrives from upstream. The * {@link Subscriber} is called with the emissions to downstream. * You can optionally call {@link Subscriber#isUnsubscribed()} to * check if you can stop emitting from the transition. If you do * wish to terminate the Observable then call * {@link Subscriber#unsubscribe()} and return anything (say * {@code null} from the transition (as the next state which will * not be used). You can also complete the Observable by calling * {@link Subscriber#onCompleted} or {@link Subscriber#onError} * from within the transition and return anything from the * transition (will not be used). The transition should run * synchronously so that completion of a call to the transition * should also signify all emissions from that transition have * been made. * @param completion * defines activity that should happen based on the final state * just before downstream onCompleted() is called. * For example any buffered emissions in state could be emitted * at this point. Don't call observer.onCompleted() * as it is called for you after the action completes if and only * if you return true from this function. * @param backpressureStrategy * is applied to the emissions from one call of transition and * should enforce backpressure. * @param * the class representing the state of the state machine * @param * the input observable type * @param * the output observable type * @throws NullPointerException * if {@code initialStateFactory} or {@code transition},or * {@code completionAction} is null * @return a backpressure supporting transformer that implements the state * machine specified by the parameters */ public static Transformer stateMachine( Func0 initialStateFactory, Func3, ? extends State> transition, Func2, Boolean> completion, BackpressureStrategy backpressureStrategy) { return TransformerStateMachine. create(initialStateFactory, transition, completion, backpressureStrategy, DEFAULT_INITIAL_BATCH); } public static Transformer stateMachine( Func0 initialStateFactory, Func3, ? extends State> transition, Func2, Boolean> completion, BackpressureStrategy backpressureStrategy, int initialRequest) { return TransformerStateMachine. create(initialStateFactory, transition, completion, backpressureStrategy, initialRequest); } /** *

* Returns a {@link Transformer} that allows processing of the source stream * to be defined in a state machine where transitions of the state machine * may also emit items to downstream that are buffered if necessary when * backpressure is requested. flatMap is part of the processing * chain so the source may experience requests for more items than are * strictly required by the endpoint subscriber. The backpressure strategy * used for emissions from the transition into the flatMap is * {@link BackpressureStrategy#BUFFER} which corresponds to * {@link Observable#onBackpressureBuffer}. * *

* marble diagram * * @param initialStateFactory * the factory to create the initial state of the state machine. * @param transition * defines state transitions and consequent emissions to * downstream when an item arrives from upstream. The * {@link Subscriber} is called with the emissions to downstream. * You can optionally call {@link Subscriber#isUnsubscribed()} to * check if you can stop emitting from the transition. If you do * wish to terminate the Observable then call * {@link Subscriber#unsubscribe()} and return anything (say * {@code null} from the transition (as the next state which will * not be used). You can also complete the Observable by calling * {@link Subscriber#onCompleted} or {@link Subscriber#onError} * from within the transition and return anything from the * transition (will not be used). The transition should run * synchronously so that completion of a call to the transition * should also signify all emissions from that transition have * been made. * @param completion * defines activity that should happen based on the final state * just before downstream onCompleted() is called. * For example any buffered emissions in state could be emitted * at this point. Don't call observer.onCompleted() * as it is called for you after the action completes if and only * if you return true from this function. * @param * the class representing the state of the state machine * @param * the input observable type * @param * the output observable type * @throws NullPointerException * if {@code initialStateFactory} or {@code transition},or * {@code completion} is null * @return a backpressure supporting transformer that implements the state * machine specified by the parameters */ public static Transformer stateMachine( Func0 initialStateFactory, Func3, ? extends State> transition, Func2, Boolean> completion) { return TransformerStateMachine. create(initialStateFactory, transition, completion, BackpressureStrategy.BUFFER, DEFAULT_INITIAL_BATCH); } public static StateMachine.Builder stateMachine() { return StateMachine.builder(); } /** *

* Returns the source {@link Observable} merged with the other * observable using the given {@link Comparator} for order. A precondition * is that the source and other are already ordered. This transformer * supports backpressure and its inputs must also support backpressure. * *

* marble diagram * * @param other * the other already ordered observable * @param comparator * the ordering to use * @param * the generic type of the objects being compared * @return merged and ordered observable */ public static final Transformer orderedMergeWith(final Observable other, final Comparator comparator) { @SuppressWarnings("unchecked") Collection> collection = Arrays.asList(other); return orderedMergeWith(collection, comparator); } /** *

* Returns the source {@link Observable} merged with all of the other * observables using the given {@link Comparator} for order. A precondition * is that the source and other are already ordered. This transformer * supports backpressure and its inputs must also support backpressure. * *

* marble diagram * * @param others * a collection of already ordered observables to merge with * @param comparator * the ordering to use * @param * the generic type of the objects being compared * @return merged and ordered observable */ public static final Transformer orderedMergeWith( final Collection> others, final Comparator comparator) { return new Transformer() { @Override public Observable call(Observable source) { List> collection = new ArrayList>(); collection.add(source); collection.addAll(others); return OrderedMerge. create(collection, comparator, false); } }; } /** * Returns a {@link Transformer} that returns an {@link Observable} that is * a buffering of the source Observable into lists of sequential items that * are equal. * *

* For example, the stream * {@code Observable.just(1, 1, 2, 2, 1).compose(toListUntilChanged())} * would emit {@code [1,1], [2], [1]}. * * @param * the generic type of the source Observable * @return transformer as above */ public static Transformer> toListUntilChanged() { Func2, T, Boolean> equal = HolderEquals.instance(); return toListWhile(equal); } private static class HolderEquals { private static final Func2, Object, Boolean> INSTANCE = new Func2, Object, Boolean>() { @Override public Boolean call(Collection list, Object t) { return list.isEmpty() || list.iterator().next().equals(t); } }; @SuppressWarnings("unchecked") static Func2, T, Boolean> instance() { return (Func2, T, Boolean>) (Func2) INSTANCE; } } /** *

* Returns a {@link Transformer} that returns an {@link Observable} that is * a buffering of the source Observable into lists of sequential items that * satisfy the condition {@code condition}. * *

* marble diagram * * @param condition * condition function that must return true if an item is to be * part of the list being prepared for emission * @param * the generic type of the source Observable * @return transformer as above */ public static Transformer> toListWhile( final Func2, ? super T, Boolean> condition) { Func0> initialState = new Func0>() { @Override public List call() { return new ArrayList(); } }; Action2, T> collect = new Action2, T>() { @Override public void call(List list, T n) { list.add(n); } }; return collectWhile(initialState, collect, condition); } /** *

* Returns a {@link Transformer} that returns an {@link Observable} that is * collected into {@code Collection} instances created by {@code factory} * that are emitted when the collection and latest emission do not satisfy * {@code condition} or on completion. * *

* marble diagram * * @param factory * collection instance creator * @param collect * collection action * @param condition * returns true if and only if emission should be collected in * current collection being prepared for emission * @param isEmpty * indicates that the collection is empty * @param * generic type of source observable * @param * collection type emitted by transformed Observable * @return transformer as above */ public static Transformer collectWhile(final Func0 factory, final Action2 collect, final Func2 condition, final Func1 isEmpty) { Func3, R> transition = new Func3, R>() { @Override public R call(R collection, T t, Observer observer) { if (condition.call(collection, t)) { collect.call(collection, t); return collection; } else { observer.onNext(collection); R r = factory.call(); collect.call(r, t); return r; } } }; Func2, Boolean> completionAction = new Func2, Boolean>() { @Override public Boolean call(R collection, Observer observer) { if (!isEmpty.call(collection)) { observer.onNext(collection); } return true; } }; return Transformers.stateMachine(factory, transition, completionAction); } /** *

* Returns a {@link Transformer} that returns an {@link Observable} that is * collected into {@code Collection} instances created by {@code factory} * that are emitted when items are not equal or on completion. * *

* marble diagram * * @param factory * collection instance creator * @param collect * collection action * @param * generic type of source observable * @param * collection type emitted by transformed Observable * @return transformer as above */ public static > Transformer collectWhile( final Func0 factory, final Action2 collect) { return collectWhile(factory, collect, HolderEquals. instance()); } public static > Transformer collectWhile(final Func0 factory, final Action2 collect, final Func2 condition) { Func1 isEmpty = new Func1() { @Override public Boolean call(R collection) { return !collection.iterator().hasNext(); } }; return collectWhile(factory, collect, condition, isEmpty); } /** * Returns a {@link Transformer} that applied to a source {@link Observable} * calls the given action on the {@code n}th onNext emission. * * @param n * the 1-based count of onNext to do the action on * @param action * is performed on {@code n}th onNext. * @param * the generic type of the Observable being transformed * @return Transformer that applied to a source Observable calls the given * action on the nth onNext emission. */ public static Transformer doOnNext(final int n, final Action1 action) { return new Transformer() { @Override public Observable call(Observable o) { return o.lift(OperatorDoOnNth.create(action, n)); } }; } /** * Returns a {@link Transformer} that applied to a source {@link Observable} * calls the given action on the first onNext emission. * * @param action * is performed on first onNext * @param * the generic type of the Observable being transformed * @return Transformer that applied to a source Observable calls the given * action on the first onNext emission. */ public static Transformer doOnFirst(final Action1 action) { return doOnNext(1, action); } /** *

* Returns an observable that subscribes to {@code this} and wait for * completion but doesn't emit any items and once completes emits the * {@code next} observable. * *

* marble diagram * * @param * input observable type * @param * output observable type * @param next * observable to be emitted after ignoring elements of * {@code this} * @return Transformer that applied to a source Observable ignores the * elements of the source and emits the elements of a second * observable */ public static Transformer ignoreElementsThen(final Observable next) { return new Transformer() { @SuppressWarnings("unchecked") @Override public Observable call(Observable source) { return ((Observable) (Observable) source.ignoreElements()).concatWith(next); } }; } public static Transformer split(String pattern) { return TransformerStringSplit.split(pattern, null); } public static Transformer split(Pattern pattern) { return TransformerStringSplit.split(null, pattern); } /** *

* Decodes a stream of multibyte chunks into a stream of strings that works * on infinite streams and handles when a multibyte character spans two * chunks. This method allows for more control over how malformed and * unmappable characters are handled. *

* * * @param charsetDecoder * decodes the bytes into strings * @return the Observable returning a stream of decoded strings */ public static Transformer decode(final CharsetDecoder charsetDecoder) { return TransformerDecode.decode(charsetDecoder); } public static Transformer limitSubscribers(AtomicInteger subscriberCount, int maxSubscribers) { return new TransformerLimitSubscribers(subscriberCount, maxSubscribers); } public static Transformer limitSubscribers(int maxSubscribers) { return new TransformerLimitSubscribers(new AtomicInteger(), maxSubscribers); } public static Transformer cache(final long duration, final TimeUnit unit, final Worker worker) { return new Transformer() { @Override public Observable call(Observable o) { return Obs.cache(o, duration, unit, worker); } }; } public static Transformer sampleFirst(final long duration, final TimeUnit unit) { return sampleFirst(duration, unit, Schedulers.computation()); } public static Transformer sampleFirst(final long duration, final TimeUnit unit, final Scheduler scheduler) { if (duration <= 0) { throw new IllegalArgumentException("duration must be > 0"); } return new Transformer() { @Override public Observable call(Observable source) { return source.lift(new OperatorSampleFirst(duration, unit, scheduler)); } }; } public static Transformer onBackpressureBufferToFile() { return onBackpressureBufferToFile(DataSerializers. javaIO(), Schedulers.computation(), Options.defaultInstance()); } public static Transformer onBackpressureBufferToFile( final DataSerializer serializer) { return onBackpressureBufferToFile(serializer, Schedulers.computation(), Options.defaultInstance()); } public static Transformer onBackpressureBufferToFile( final DataSerializer serializer, final Scheduler scheduler) { return onBackpressureBufferToFile(serializer, scheduler, Options.defaultInstance()); } public static Transformer onBackpressureBufferToFile( final DataSerializer serializer, final Scheduler scheduler, final Options options) { return new Transformer() { @Override public Observable call(Observable o) { return o.lift(new OperatorBufferToFile(serializer, scheduler, options)); } }; } public static Transformer windowMin(final int windowSize, final Comparator comparator) { return new Transformer() { @Override public Observable call(Observable o) { return o.lift(new OperatorWindowMinMax(windowSize, comparator, Metric.MIN)); } }; } public static > Transformer windowMax(final int windowSize) { return windowMax(windowSize, Transformers. naturalComparator()); } public static Transformer windowMax(final int windowSize, final Comparator comparator) { return new Transformer() { @Override public Observable call(Observable o) { return o.lift(new OperatorWindowMinMax(windowSize, comparator, Metric.MAX)); } }; } public static > Transformer windowMin(final int windowSize) { return windowMin(windowSize, Transformers. naturalComparator()); } private static class NaturalComparatorHolder { static final Comparator> INSTANCE = new Comparator>() { @Override public int compare(Comparable o1, Comparable o2) { return o1.compareTo(o2); } }; } @SuppressWarnings("unchecked") private static > Comparator naturalComparator() { return (Comparator) (Comparator) NaturalComparatorHolder.INSTANCE; } /** *

* Groups the items emitted by an {@code Observable} according to a * specified criterion, and emits these grouped items as * {@link GroupedObservable}s. The emitted {@code GroupedObservable} allows * only a single {@link Subscriber} during its lifetime and if this * {@code Subscriber} unsubscribes before the source terminates, the next * emission by the source having the same key will trigger a new * {@code GroupedObservable} emission. *

* *

* Note: A {@link GroupedObservable} will cache the items it is to * emit until such time as it is subscribed to. For this reason, in order to * avoid memory leaks, you should not simply ignore those * {@code GroupedObservable}s that do not concern you. Instead, you can * signal to them that they may discard their buffers by applying an * operator like {@code .ignoreElements()} to them. *

*
Scheduler:
*
{@code groupBy} does not operate by default on a particular * {@link Scheduler}.
*
* * @param keySelector * a function that extracts the key for each item * @param elementSelector * a function that extracts the return element for each item * @param evictingMapFactory * a function that given an eviction action returns a {@link Map} * instance that will be used to assign items to the appropriate * {@code GroupedObservable}s. The {@code Map} instance must be * thread-safe and any eviction must trigger a call to the * supplied action (synchronously or asynchronously). This can be * used to limit the size of the map by evicting keys by maximum * size or access time for instance. If * {@code evictingMapFactory} is null then no eviction strategy * will be applied (and a suitable default thread-safe * implementation of {@code Map} will be supplied). Here's an * example using Guava's {@code CacheBuilder} from v19.0: * *
     *            {@code
     *            Func1, Map> mapFactory 
     *              = action -> CacheBuilder.newBuilder()
     *                  .maximumSize(1000)
     *                  .expireAfterAccess(12, TimeUnit.HOUR)
     *                  .removalListener(key -> action.call(key))
     *                  . build().asMap();
     *            }
     *            
* * @param * the type of the input observable * @param * the key type * @param * the element type * @return an {@code Observable} that emits {@link GroupedObservable}s, each * of which corresponds to a unique key value and each of which * emits those items from the source Observable that share that key * value * @see * ReactiveX operators documentation: GroupBy */ public static Transformer> groupByEvicting( final Func1 keySelector, final Func1 elementSelector, final Func1, Map> evictingMapFactory) { return new Transformer>() { @Override public Observable> call(Observable o) { return o.groupBy(keySelector, elementSelector, evictingMapFactory); } }; } /** * If multiple concurrently open subscriptions happen to a source * transformed by this method then an additional do-nothing subscription * will be maintained to the source and will only be closed after the * specified duration has passed from the final unsubscription of the open * subscriptions. If another subscription happens during this wait period * then the scheduled unsubscription will be cancelled. * * @param duration * duration of period to leave at least one source subscription * open * @param unit * units for duration * @param * generic type of stream * @return transformer */ public static Transformer delayFinalUnsubscribe(long duration, TimeUnit unit) { return delayFinalUnsubscribe(duration, unit, Schedulers.computation()); } /** * If multiple concurrently open subscriptions happen to a source * transformed by this method then an additional do-nothing subscription * will be maintained to the source and will only be closed after the * specified duration has passed from the final unsubscription of the open * subscriptions. If another subscription happens during this wait period * then the scheduled unsubscription will be cancelled. * * @param duration * duration of period to leave at least one source subscription * open * @param unit * units for duration * @param scheduler * scheduler to use to schedule wait for unsubscribe * @param * generic type of stream * @return transformer */ public static Transformer delayFinalUnsubscribe(long duration, TimeUnit unit, Scheduler scheduler) { return new TransformerDelayFinalUnsubscribe(unit.toMillis(duration), scheduler); } /** * Removes pairs non-recursively from a stream. Uses * {@code Transformers.stateMachine()} under the covers to ensure items are * emitted as soon as possible (if an item can't be in a pair then it is * emitted straight away). * * @param isCandidateForFirst * returns true if item is potentially the first of a pair that * we want to remove * @param remove * returns true if a pair should be removed * @param * generic type of stream being transformed * @return transformed stream */ public static Transformer removePairs( final Func1 isCandidateForFirst, final Func2 remove) { return new Transformer() { @Override public Observable call(Observable o) { return o.compose(Transformers. // stateMachine() // .initialState(Optional. absent()) // .transition(new Transition, T, T>() { @Override public Optional call(Optional state, T value, Subscriber subscriber) { if (!state.isPresent()) { if (isCandidateForFirst.call(value)) { return Optional.of(value); } else { subscriber.onNext(value); return Optional.absent(); } } else { if (remove.call(state.get(), value)) { // emit nothing and reset state return Optional.absent(); } else { subscriber.onNext(state.get()); if (isCandidateForFirst.call(value)) { return Optional.of(value); } else { subscriber.onNext(value); return Optional.absent(); } } } } }).completion(new Completion, T>() { @Override public Boolean call(Optional state, Subscriber subscriber) { if (state.isPresent()) subscriber.onNext(state.get()); // yes, complete return true; } }).build()); } }; } /** * Rather than requesting {@code Long.MAX_VALUE} of upstream as does * `Observable.onBackpressureBuffer`, this variant only requests of upstream * what is requested of it. Thus an operator can be written that * overproduces. * * @param * the value type * @return transformer that buffers on backpressure but only requests of * upstream what is requested of it */ public static Transformer onBackpressureBufferRequestLimiting() { return TransformerOnBackpressureBufferRequestLimiting.instance(); } /** * Buffers the elements into continuous, non-overlapping Lists where the * boundary is determined by a predicate receiving each item, after being * buffered, and returns true to indicate a new buffer should start. * *

* The operator won't return an empty first or last buffer. * *

*
Backpressure Support:
*
This operator supports backpressure.
*
Scheduler:
*
This operator does not operate by default on a particular * {@link Scheduler}.
*
* * @param * the input value type * @param predicate * the Func1 that receives each item, after being buffered, and * should return true to indicate a new buffer has to start. * @return the new Observable instance * @see #bufferWhile(Func1) * @since (if this graduates from Experimental/Beta to supported, replace * this parenthetical with the release number) */ public static final Transformer> bufferUntil( Func1 predicate) { return bufferUntil(predicate, 10); } /** * Buffers the elements into continuous, non-overlapping Lists where the * boundary is determined by a predicate receiving each item, after being * buffered, and returns true to indicate a new buffer should start. * *

* The operator won't return an empty first or last buffer. * *

*
Backpressure Support:
*
This operator supports backpressure.
*
Scheduler:
*
This operator does not operate by default on a particular * {@link Scheduler}.
*
* * @param * the input value type * @param predicate * the Func1 that receives each item, after being buffered, and * should return true to indicate a new buffer has to start. * @return the new Observable instance * @see #bufferWhile(Func1) * @since (if this graduates from Experimental/Beta to supported, replace * this parenthetical with the release number) */ public static final Transformer> toListUntil( Func1 predicate) { return bufferUntil(predicate); } /** * Buffers the elements into continuous, non-overlapping Lists where the * boundary is determined by a predicate receiving each item, after being * buffered, and returns true to indicate a new buffer should start. * *

* The operator won't return an empty first or last buffer. * *

*
Backpressure Support:
*
This operator supports backpressure.
*
Scheduler:
*
This operator does not operate by default on a particular * {@link Scheduler}.
*
* * @param * the input value type * @param predicate * the Func1 that receives each item, after being buffered, and * should return true to indicate a new buffer has to start. * @param capacityHint * the expected number of items in each buffer * @return the new Observable instance * @see #bufferWhile(Func1) * @since (if this graduates from Experimental/Beta to supported, replace * this parenthetical with the release number) */ public static final Transformer> bufferUntil(Func1 predicate, int capacityHint) { return new OperatorBufferPredicateBoundary(predicate, RxRingBuffer.SIZE, capacityHint, true); } /** * Buffers the elements into continuous, non-overlapping Lists where the * boundary is determined by a predicate receiving each item, after being * buffered, and returns true to indicate a new buffer should start. * *

* The operator won't return an empty first or last buffer. * *

*
Backpressure Support:
*
This operator supports backpressure.
*
Scheduler:
*
This operator does not operate by default on a particular * {@link Scheduler}.
*
* * @param * the input value type * @param predicate * the Func1 that receives each item, after being buffered, and * should return true to indicate a new buffer has to start. * @param capacityHint * the expected number of items in each buffer * @return the new Observable instance * @see #bufferWhile(Func1) * @since (if this graduates from Experimental/Beta to supported, replace * this parenthetical with the release number) */ public static final Transformer> toListUntil(Func1 predicate, int capacityHint) { return bufferUntil(predicate, capacityHint); } /** * Buffers the elements into continuous, non-overlapping Lists where the * boundary is determined by a predicate receiving each item, before or * after being buffered, and returns true to indicate a new buffer should * start. * *

* The operator won't return an empty first or last buffer. * *

*
Backpressure Support:
*
This operator supports backpressure.
*
Scheduler:
*
This operator does not operate by default on a particular * {@link Scheduler}.
*
* * @param * the input value type * @param predicate * the Func1 that receives each item, before being buffered, and * should return true to indicate a new buffer has to start. * @return the new Observable instance * @see #bufferWhile(Func1) * @since (if this graduates from Experimental/Beta to supported, replace * this parenthetical with the release number) */ public static final Transformer> bufferWhile( Func1 predicate) { return bufferWhile(predicate, 10); } /** * Buffers the elements into continuous, non-overlapping Lists where the * boundary is determined by a predicate receiving each item, before or * after being buffered, and returns true to indicate a new buffer should * start. * *

* The operator won't return an empty first or last buffer. * *

*
Backpressure Support:
*
This operator supports backpressure.
*
Scheduler:
*
This operator does not operate by default on a particular * {@link Scheduler}.
*
* * @param * the input value type * @param predicate * the Func1 that receives each item, before being buffered, and * should return true to indicate a new buffer has to start. * @return the new Observable instance * @see #bufferWhile(Func1) * @since (if this graduates from Experimental/Beta to supported, replace * this parenthetical with the release number) */ public static final Transformer> toListWhile( Func1 predicate) { return bufferWhile(predicate); } /** * Buffers the elements into continuous, non-overlapping Lists where the * boundary is determined by a predicate receiving each item, before being * buffered, and returns true to indicate a new buffer should start. * *

* The operator won't return an empty first or last buffer. * *

*
Backpressure Support:
*
This operator supports backpressure.
*
Scheduler:
*
This operator does not operate by default on a particular * {@link Scheduler}.
*
* * @param * the input value type * @param predicate * the Func1 that receives each item, before being buffered, and * should return true to indicate a new buffer has to start. * @param capacityHint * the expected number of items in each buffer * @return the new Observable instance * @see #bufferWhile(Func1) * @since (if this graduates from Experimental/Beta to supported, replace * this parenthetical with the release number) */ public static final Transformer> bufferWhile(Func1 predicate, int capacityHint) { return new OperatorBufferPredicateBoundary(predicate, RxRingBuffer.SIZE, capacityHint, false); } /** * Buffers the elements into continuous, non-overlapping Lists where the * boundary is determined by a predicate receiving each item, before being * buffered, and returns true to indicate a new buffer should start. * *

* The operator won't return an empty first or last buffer. * *

*
Backpressure Support:
*
This operator supports backpressure.
*
Scheduler:
*
This operator does not operate by default on a particular * {@link Scheduler}.
*
* * @param * the input value type * @param predicate * the Func1 that receives each item, before being buffered, and * should return true to indicate a new buffer has to start. * @param capacityHint * the expected number of items in each buffer * @return the new Observable instance * @see #bufferWhile(Func1) * @since (if this graduates from Experimental/Beta to supported, replace * this parenthetical with the release number) */ public static final Transformer> toListWhile(Func1 predicate, int capacityHint) { return bufferWhile(predicate, capacityHint); } public static final Transformer delay(final Func1 time, final Func0 playRate, final long startTime, final Scheduler scheduler) { return new Transformer() { @Override public Observable call(final Observable o) { return Observable.defer(new Func0>() { long startActual = scheduler.now(); @Override public Observable call() { return o.concatMap(new Func1>() { @Override public Observable call(T t) { return Observable.just(t) // .delay(delay(startActual, startTime, time.call(t), playRate, scheduler.now()), TimeUnit.MILLISECONDS, scheduler); } }); } }); } }; } private static long delay(long startActual, long startTime, long emissionTimestamp, Func0 playRate, long now) { long elapsedActual = now - startActual; return Math.max(0, Math.round((emissionTimestamp - startTime) / playRate.call() - elapsedActual)); } /** *

Modifies the source Observable so that it invokes an action when it calls {@code onCompleted} and no items were emitted. *

*
Scheduler:
*
{@code doOnEmpty} does not operate by default on a particular {@link Scheduler}.
*
* * @param onEmpty * the action to invoke when the source Observable calls {@code onCompleted}, contingent on no items were emitted * @param generic type of observable being transformed * @return the source Observable with the side-effecting behavior applied */ public static final Transformer doOnEmpty(final Action0 onEmpty) { return new Transformer () { @Override public Observable call(Observable o) { return Observable.create(new OnSubscribeDoOnEmpty(o, onEmpty)); }}; } public static final Transformer onTerminateResume( final Func1> onError, final Observable onCompleted) { return new TransformerOnTerminateResume(onError, onCompleted); } public static final Transformer repeatLast() { return new Transformer() { @Override public Observable call(Observable o) { return o.materialize().buffer(2, 1).flatMap(new Func1>, Observable>() { @Override public Observable call(List> list) { Notification a = list.get(0); if (list.size() ==2 && list.get(1).isOnCompleted()) { return Observable.just(a.getValue()).repeat(); } else if (a.isOnError()) { return Observable.error(list.get(0).getThrowable()); } else if (a.isOnCompleted()) { return Observable.empty(); } else { return Observable.just(a.getValue()); } } }); }}; } }