package com.raizlabs.android.dbflow.rx2.language;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import com.raizlabs.android.dbflow.config.FlowLog;
import com.raizlabs.android.dbflow.list.FlowCursorIterator;
import com.raizlabs.android.dbflow.sql.language.CursorResult;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import java.util.concurrent.atomic.AtomicLong;
import io.reactivex.Flowable;
import io.reactivex.SingleObserver;
import io.reactivex.disposables.Disposable;
/**
* Description: Wraps a {@link RXModelQueriable} into a {@link Flowable}
* for each element represented by the query.
*/
public class CursorResultFlowable<T> extends Flowable<T> {
@NonNull
private final RXModelQueriable<T> modelQueriable;
public CursorResultFlowable(@NonNull RXModelQueriable<T> modelQueriable) {
this.modelQueriable = modelQueriable;
}
@Override
protected void subscribeActual(final Subscriber<? super T> subscriber) {
subscriber.onSubscribe(new Subscription() {
@Override
public void request(final long n) {
modelQueriable.queryResults().subscribe(new CursorResultObserver<>(subscriber, n));
}
@Override
public void cancel() {
}
});
}
@VisibleForTesting
static class CursorResultObserver<T> implements SingleObserver<CursorResult<T>> {
private final Subscriber<? super T> subscriber;
private final long count;
private final AtomicLong emitted;
private final AtomicLong requested;
private Disposable disposable;
CursorResultObserver(Subscriber<? super T> subscriber, long count) {
this.subscriber = subscriber;
this.count = count;
requested = new AtomicLong();
emitted = new AtomicLong();
}
@Override
public void onSubscribe(Disposable disposable) {
this.disposable = disposable;
}
@Override
public void onSuccess(CursorResult<T> ts) {
int starting = this.count == Long.MAX_VALUE && requested.compareAndSet(0, Long.MAX_VALUE)
? 0 : emitted.intValue();
long limit = this.count + starting;
while (limit > 0) {
FlowCursorIterator<T> iterator = ts.iterator(starting, limit);
try {
long i = 0;
while (!disposable.isDisposed() && iterator.hasNext() && i++ < limit) {
subscriber.onNext(iterator.next());
}
emitted.addAndGet(i);
// no more items
if (!disposable.isDisposed() && i < limit) {
subscriber.onComplete();
break;
}
limit = requested.addAndGet(-limit);
} catch (Exception e) {
FlowLog.logError(e);
subscriber.onError(e);
} finally {
try {
iterator.close();
} catch (Exception e) {
FlowLog.logError(e);
subscriber.onError(e);
}
}
}
}
@Override
public void onError(Throwable e) {
subscriber.onError(e);
}
}
}