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

rx.operators.OperationTakeLast Maven / Gradle / Ivy

There is a newer version: 0.20.7
Show newest version
/**
 * Copyright 2014 Netflix, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package rx.operators;

import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import rx.Observable;
import rx.Observable.OnSubscribeFunc;
import rx.Observer;
import rx.Scheduler;
import rx.Subscription;
import rx.schedulers.Timestamped;

/**
 * Returns an Observable that emits the last count items emitted by the source
 * Observable.
 * 

* */ public final class OperationTakeLast { public static OnSubscribeFunc takeLast(final Observable items, final int count) { return new OnSubscribeFunc() { @Override public Subscription onSubscribe(Observer observer) { return new TakeLast(items, count).onSubscribe(observer); } }; } private static class TakeLast implements OnSubscribeFunc { private final int count; private final Observable items; private final SafeObservableSubscription subscription = new SafeObservableSubscription(); TakeLast(final Observable items, final int count) { this.count = count; this.items = items; } public Subscription onSubscribe(Observer observer) { if (count < 0) { throw new IndexOutOfBoundsException( "count could not be negative"); } return subscription.wrap(items.subscribe(new ItemObserver(observer))); } private class ItemObserver implements Observer { /** * Store the last count elements until now. */ private Deque deque = new LinkedList(); private final Observer observer; private final ReentrantLock lock = new ReentrantLock(); public ItemObserver(Observer observer) { this.observer = observer; } @Override public void onCompleted() { try { for (T value : deque) { observer.onNext(value); } observer.onCompleted(); } catch (Throwable e) { observer.onError(e); } } @Override public void onError(Throwable e) { observer.onError(e); } @Override public void onNext(T value) { if (count == 0) { // If count == 0, we do not need to put value into deque and // remove it at once. We can ignore the value directly. return; } lock.lock(); try { deque.offerLast(value); if (deque.size() > count) { // Now deque has count + 1 elements, so the first // element in the deque definitely does not belong // to the last count elements of the source // sequence. We can drop it now. deque.removeFirst(); } } catch (Throwable e) { observer.onError(e); subscription.unsubscribe(); } finally { lock.unlock(); } } } } /** * Returns the items emitted by source whose arrived in the time window * before the source completed. */ public static OnSubscribeFunc takeLast(Observable source, long time, TimeUnit unit, Scheduler scheduler) { return new TakeLastTimed(source, -1, time, unit, scheduler); } /** * Returns the items emitted by source whose arrived in the time window * before the source completed and at most count values. */ public static OnSubscribeFunc takeLast(Observable source, int count, long time, TimeUnit unit, Scheduler scheduler) { return new TakeLastTimed(source, count, time, unit, scheduler); } /** Take only the values which appeared some time before the completion. */ static final class TakeLastTimed implements OnSubscribeFunc { final Observable source; final long ageMillis; final Scheduler scheduler; final int count; public TakeLastTimed(Observable source, int count, long time, TimeUnit unit, Scheduler scheduler) { this.source = source; this.ageMillis = unit.toMillis(time); this.scheduler = scheduler; this.count = count; } @Override public Subscription onSubscribe(Observer t1) { SafeObservableSubscription sas = new SafeObservableSubscription(); sas.wrap(source.subscribe(new TakeLastTimedObserver(t1, sas, count, ageMillis, scheduler))); return sas; } } /** Observes source values and keeps the most recent items. */ static final class TakeLastTimedObserver implements Observer { final Observer observer; final Subscription cancel; final long ageMillis; final Scheduler scheduler; /** -1 indicates unlimited buffer. */ final int count; final Deque> buffer = new LinkedList>(); public TakeLastTimedObserver(Observer observer, Subscription cancel, int count, long ageMillis, Scheduler scheduler) { this.observer = observer; this.cancel = cancel; this.ageMillis = ageMillis; this.scheduler = scheduler; this.count = count; } protected void runEvictionPolicy(long now) { // trim size while (count >= 0 && buffer.size() > count) { buffer.pollFirst(); } // remove old entries while (!buffer.isEmpty()) { Timestamped v = buffer.peekFirst(); if (v.getTimestampMillis() < now - ageMillis) { buffer.pollFirst(); } else { break; } } } @Override public void onNext(T args) { long t = scheduler.now(); buffer.add(new Timestamped(t, args)); runEvictionPolicy(t); } @Override public void onError(Throwable e) { buffer.clear(); observer.onError(e); cancel.unsubscribe(); } /** * Emit the contents of the buffer. * * @return true if no exception was raised in the process */ protected boolean emitBuffer() { for (Timestamped v : buffer) { try { observer.onNext(v.getValue()); } catch (Throwable t) { buffer.clear(); observer.onError(t); return false; } } buffer.clear(); return true; } @Override public void onCompleted() { runEvictionPolicy(scheduler.now()); if (emitBuffer()) { observer.onCompleted(); } cancel.unsubscribe(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy