package com.github.davidmoten.rx;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import com.github.davidmoten.rx.internal.operators.ObservableReverse;
import com.github.davidmoten.rx.internal.operators.OnSubscribeFromQueue;
import com.github.davidmoten.rx.internal.operators.OnSubscribeMatch;
import com.github.davidmoten.rx.internal.operators.OnSubscribeRepeating;
import com.github.davidmoten.rx.internal.operators.OrderedMerge;
import com.github.davidmoten.rx.internal.operators.Permutations;
import com.github.davidmoten.rx.internal.operators.Permutations.Swap;
import com.github.davidmoten.rx.observables.CachedObservable;
import com.github.davidmoten.util.Optional;
import rx.Observable;
import rx.Scheduler;
import rx.Scheduler.Worker;
import rx.functions.Action0;
import rx.functions.Func0;
import rx.functions.Func1;
import rx.functions.Func2;
public final class Obs {
/**
* Returns a cached {@link Observable} like {@link Observable#cache()}
* except that the cache can be reset by calling
* {@link CachedObservable#reset()}.
*
* @param source
* the observable to be cached.
* @param <T>
* the generic type of the source
* @return a cached observable whose cache can be reset.
*/
public static <T> CachedObservable<T> cache(Observable<T> source) {
return new CachedObservable<T>(source);
}
/**
* Returns a cached {@link Observable} like {@link Observable#cache()}
* except that the cache can be reset by calling
* {@link CachedObservable#reset()} and the cache will be automatically
* reset an interval after first subscription (or first subscription after
* reset). The interval is defined by {@code duration} and {@code unit} .
*
* @param source
* the source observable
* @param duration
* duration till next reset
* @param unit
* units corresponding to the duration
* @param worker
* worker to use for scheduling reset. Don't forget to
* unsubscribe the worker when no longer required.
* @param <T>
* the generic type of the source
* @return cached observable that resets regularly on a time interval
*/
public static <T> Observable<T> cache(final Observable<T> source, final long duration,
final TimeUnit unit, final Worker worker) {
final AtomicReference<CachedObservable<T>> cacheRef = new AtomicReference<CachedObservable<T>>();
CachedObservable<T> cache = new CachedObservable<T>(source);
cacheRef.set(cache);
return cache.doOnSubscribe(new Action0() {
@Override
public void call() {
Action0 action = new Action0() {
@Override
public void call() {
cacheRef.get().reset();
}
};
worker.schedule(action, duration, unit);
}
});
}
/**
* Returns a cached {@link Observable} like {@link Observable#cache()}
* except that the cache may be reset by the user calling
* {@link CloseableObservableWithReset#reset}.
*
* @param source
* the source observable
* @param duration
* duration till next reset
* @param unit
* units corresponding to the duration
* @param scheduler
* scheduler to use for scheduling reset.
* @param <T>
* generic type of source observable
* @return {@link CloseableObservableWithReset} that should be closed once
* finished to prevent worker memory leak.
*/
public static <T> CloseableObservableWithReset<T> cache(final Observable<T> source,
final long duration, final TimeUnit unit, final Scheduler scheduler) {
final AtomicReference<CachedObservable<T>> cacheRef = new AtomicReference<CachedObservable<T>>();
final AtomicReference<Optional<Worker>> workerRef = new AtomicReference<Optional<Worker>>(
Optional.<Worker> absent());
CachedObservable<T> cache = new CachedObservable<T>(source);
cacheRef.set(cache);
Action0 closeAction = new Action0() {
@Override
public void call() {
while (true) {
Optional<Worker> w = workerRef.get();
if (w == null) {
// we are finished
break;
} else {
if (workerRef.compareAndSet(w, null)) {
if (w.isPresent()) {
w.get().unsubscribe();
}
// we are finished
workerRef.set(null);
break;
}
}
// if not finished then try again
}
}
};
Action0 resetAction = new Action0() {
@Override
public void call() {
startScheduledResetAgain(duration, unit, scheduler, cacheRef, workerRef);
}
};
return new CloseableObservableWithReset<T>(cache, closeAction, resetAction);
}
private static <T> void startScheduledResetAgain(final long duration, final TimeUnit unit,
final Scheduler scheduler, final AtomicReference<CachedObservable<T>> cacheRef,
final AtomicReference<Optional<Worker>> workerRef) {
Action0 action = new Action0() {
@Override
public void call() {
cacheRef.get().reset();
}
};
// CAS loop to cancel the current worker and create a new one
while (true) {
Optional<Worker> wOld = workerRef.get();
if (wOld == null) {
// we are finished
return;
}
Optional<Worker> w = Optional.of(scheduler.createWorker());
if (workerRef.compareAndSet(wOld, w)) {
if (wOld.isPresent())
wOld.get().unsubscribe();
w.get().schedule(action, duration, unit);
break;
}
}
}
/**
* Returns an Observable that epeats emitting {@code t} without completing.
* Supports backpressure.
*
* @param t
* value to repeat
* @param <T>
* type of t
*
* @return an observable that repeats t forever (or until unsubscribed)
*/
public static <T> Observable<T> repeating(final T t) {
return Observable.create(new OnSubscribeRepeating<T>(t));
}
public static <T extends Comparable<? super T>> Observable<T> create(
Collection<Observable<T>> sources) {
return create(sources, false);
}
public static <T> Observable<T> create(Collection<Observable<T>> sources,
Comparator<? super T> comparator) {
return create(sources, comparator, false);
}
public static <T extends Comparable<? super T>> Observable<T> create(
Collection<Observable<T>> sources, boolean delayErrors) {
return OrderedMerge.create(sources, delayErrors);
}
public static <T> Observable<T> create(Collection<Observable<T>> sources,
Comparator<? super T> comparator, boolean delayErrors) {
return OrderedMerge.create(sources, comparator, delayErrors);
}
public static <T> Observable<T> fromQueue(Queue<T> queue) {
return Observable.create(new OnSubscribeFromQueue<T>(queue));
}
public static <T> Observable<List<Integer>> permutations(int size) {
List<Integer> indexes = new ArrayList<Integer>(size);
for (int i = 0; i < size; i++) {
indexes.add(i);
}
return Observable.from(Permutations.iterable(indexes)).scan(indexes,
new Func2<List<Integer>, Swap<Integer>, List<Integer>>() {
@Override
public List<Integer> call(List<Integer> a, Swap<Integer> swap) {
List<Integer> b = new ArrayList<Integer>(a);
b.set(swap.left(), a.get(swap.right()));
b.set(swap.right(), a.get(swap.left()));
return b;
}
});
}
public static <T> Observable<List<T>> permutations(final List<T> list) {
return permutations(list.size()).map(new Func1<List<Integer>, List<T>>() {
@Override
public List<T> call(List<Integer> a) {
List<T> b = new ArrayList<T>(a.size());
for (int i = 0; i < a.size(); i++) {
b.add(list.get(a.get(i)));
}
return b;
}
});
}
public static Observable<Long> intervalLong(final long duration, final TimeUnit unit,
final Scheduler scheduler) {
return Observable.defer(new Func0<Observable<Long>>() {
final long[] count = new long[1];
@Override
public Observable<Long> call() {
return Observable.interval(duration, unit, scheduler).map(new Func1<Long, Long>() {
@Override
public Long call(Long t) {
return count[0]++;
}
});
}
});
}
public static Observable<Long> intervalLong(long duration, TimeUnit unit) {
return intervalLong(duration, unit, Schedulers.computation());
}
public static <A, B, K, C> Observable<C> match(final Observable<A> a, final Observable<B> b,
final Func1<? super A, ? extends K> aKey, final Func1<? super B, ? extends K> bKey,
final Func2<? super A, ? super B, C> combiner) {
return match(a, b, aKey, bKey, combiner, 128);
}
public static <A, B, K, C> Observable<C> match(final Observable<A> a, final Observable<B> b,
final Func1<? super A, ? extends K> aKey, final Func1<? super B, ? extends K> bKey,
final Func2<? super A, ? super B, C> combiner, long requestSize) {
return Observable
.create(new OnSubscribeMatch<A, B, K, C>(a, b, aKey, bKey, combiner, requestSize));
}
public static <T> Observable<T> reverse(Observable<T> source) {
return ObservableReverse.reverse(source);
}
}