package com.github.davidmoten.rx; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import rx.Observable; import rx.Observable.OnSubscribe; import rx.Observable.Transformer; import rx.Observer; import rx.Subscriber; import rx.functions.Action1; import rx.functions.Func1; /** * Utility methods for RxJava. */ public final class RxUtil { /** * slf4j logger. */ private static final Logger log = LoggerFactory.getLogger(RxUtil.class); private RxUtil() { // prevent instantiation } /** * Returns the concatenation of two {@link Observable}s but the first * sequence will be emitted in its entirety and ignored before o2 starts * emitting. * * @param <T> * the generic type of the second observable * @param o1 * the sequence to ignore * @param o2 * the sequence to emit after o1 ignored * @return observable result of concatenating two observables, ignoring the * first */ @SuppressWarnings("unchecked") public static <T> Observable<T> concatButIgnoreFirstSequence(Observable<?> o1, Observable<T> o2) { return Observable.concat((Observable<T>) o1.ignoreElements(), o2); } /** * Logs errors and onNext at info level using slf4j {@link Logger}. * * @param <T> * the return generic type * @return a logging {@link Observer} */ public static <T> Observer<? super T> log() { return new Observer<T>() { @Override public void onCompleted() { // do nothing } @Override public void onError(Throwable e) { log.error(e.getMessage(), e); } @Override public void onNext(T t) { log.info(t + ""); } }; } /** * Returns an {@link Action1} that increments a counter when the call method * is called. * * @param <T> * generic type of item being counted * @return {@link Action1} to count calls. */ public static <T> CountingAction<T> counter() { return new CountingAction<T>(); } public static class CountingAction<T> implements Action1<T> { private final AtomicLong count = new AtomicLong(0); public Observable<Long> count() { return Observable.create(new OnSubscribe<Long>() { @Override public void call(Subscriber<? super Long> subscriber) { subscriber.onNext(count.get()); subscriber.onCompleted(); } }); } @Override public void call(T t) { count.incrementAndGet(); } } public static <T extends Number> Func1<T, Boolean> greaterThanZero() { return new Func1<T, Boolean>() { @Override public Boolean call(T t) { return t.doubleValue() > 0; } }; } /** * Returns a {@link Func1} that returns an empty {@link Observable}. * * @return */ public static <T> Func1<T, Observable<Object>> toEmpty() { return Functions.constant(Observable.<Object> empty()); } /** * Returns an {@link Transformer} that flattens a sequence of * {@link Observable} into a flat sequence of the items from the * Observables. This operator may interleave the items asynchronously. * * @return Transformer */ public static <T> Transformer<Observable<T>, T> flatten() { return new Transformer<Observable<T>, T>() { @Override public Observable<T> call(Observable<Observable<T>> source) { return source.flatMap(Functions.<Observable<T>> identity()); } }; } /** * Adds {@code n} to {@code requested} field and returns the value prior to * addition once the addition is successful (uses CAS semantics). If * overflows then sets {@code requested} field to {@code Long.MAX_VALUE}. * * @param requested * atomic field updater for a request count * @param object * contains the field updated by the updater * @param n * the number of requests to add to the requested count * @return requested value just prior to successful addition */ public static <T> long getAndAddRequest(AtomicLongFieldUpdater<T> requested, T object, long n) { // add n to field but check for overflow while (true) { long current = requested.get(object); long next = current + n; // check for overflow if (next < 0) { next = Long.MAX_VALUE; } if (requested.compareAndSet(object, current, next)) { return current; } } } /** * Adds {@code n} to {@code requested} and returns the value prior to * addition once the addition is successful (uses CAS semantics). If * overflows then sets {@code requested} field to {@code Long.MAX_VALUE}. * * @param requested * atomic field updater for a request count * @param object * contains the field updated by the updater * @param n * the number of requests to add to the requested count * @return requested value just prior to successful addition */ public static long getAndAddRequest(AtomicLong requested, long n) { // add n to field but check for overflow while (true) { long current = requested.get(); long next = current + n; // check for overflow if (next < 0) { next = Long.MAX_VALUE; } if (requested.compareAndSet(current, next)) { return current; } } } }