package au.gov.amsa.util.rx; import java.util.ArrayList; import java.util.List; import rx.Observable.Operator; import rx.Subscriber; import rx.functions.Func1; /** * Buffers items into lists that can overlap but not more than two at a time. * The commencement of the next buffer is signalled by a change in the value of * {@code startFunction} and the finish of a buffer is signalled by a change in * the value of the {@code whileFunction}. * * @param <T> * generic type */ public class OperatorDynamicBuffer<T> implements Operator<List<T>, T> { private static final Object NOT_SET = new Object(); private static final Object NULL_SENTINEL = new Object(); private final Func1<T, ?> startFunction; private final Func1<T, ?> whileFunction; /** * Constructor. * * @param startFunction * a change in the value of this function signals the start of a * new buffer * @param whileFunction * a change in the value of this function signals the finish of a * buffer */ public OperatorDynamicBuffer(Func1<T, ?> startFunction, Func1<T, ?> whileFunction) { this.startFunction = startFunction; this.whileFunction = whileFunction; } @Override public Subscriber<? super T> call(Subscriber<? super List<T>> child) { return new Subscriber<T>(child) { private List<T> list; private List<T> list2; private Object lastStartValue = NOT_SET; private Object lastWhileValue = NOT_SET; @Override public void onCompleted() { if (list != null && !isUnsubscribed()) child.onNext(list); if (list2 != null && !isUnsubscribed()) child.onNext(list2); if (!isUnsubscribed()) child.onCompleted(); } @Override public void onError(Throwable e) { child.onError(e); } @Override public void onNext(T t) { if (list == null) list = new ArrayList<T>(); Object startValue = replaceNull(startFunction.call(t)); Object whileValue = replaceNull(whileFunction.call(t)); if (lastWhileValue == NOT_SET) { list.add(t); request(1); } else { if (lastWhileValue.equals(whileValue)) { list.add(t); request(1); } else { // emit list 1 child.onNext(list); list = null; } if (lastStartValue != NOT_SET && !lastStartValue.equals(startValue)) { // startFunction value has changed so we are ready to // start another buffer if (list2 == null) { list2 = new ArrayList<T>(); } else { child.onError(new RuntimeException("unexpected")); return; } list2.add(t); } if (list == null && list2 != null) { // move second list into first position list = list2; list2 = null; } } lastStartValue = startValue; lastWhileValue = whileValue; } }; } private static Object replaceNull(Object o) { if (o == null) return NULL_SENTINEL; else return o; } }