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

rx.operators.OperationCombineLatest 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.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import rx.Observable;
import rx.Observable.OnSubscribeFunc;
import rx.Observer;
import rx.Subscription;
import rx.functions.Func2;
import rx.functions.Func3;
import rx.functions.Func4;
import rx.functions.Func5;
import rx.functions.Func6;
import rx.functions.Func7;
import rx.functions.Func8;
import rx.functions.Func9;
import rx.functions.FuncN;
import rx.functions.Functions;
import rx.subscriptions.CompositeSubscription;

/**
 * Returns an Observable that combines the emissions of multiple source observables. Once each
 * source Observable has emitted at least one item, combineLatest emits an item whenever any of
 * the source Observables emits an item, by combining the latest emissions from each source
 * Observable with a specified function.
 * 

* */ public class OperationCombineLatest { /** * Combines the two given observables, emitting an event containing an aggregation of the latest values of each of the source observables * each time an event is received from one of the source observables, where the aggregation is defined by the given function. * * @param w0 * The first source observable. * @param w1 * The second source observable. * @param combineLatestFunction * The aggregation function used to combine the source observable values. * @return A function from an observer to a subscription. This can be used to create an observable from. */ @SuppressWarnings("unchecked") public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) { return new CombineLatest(Arrays.asList(w0, w1), Functions.fromFunc(combineLatestFunction)); } /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ @SuppressWarnings("unchecked") public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Func3 combineLatestFunction) { return new CombineLatest(Arrays.asList(w0, w1, w2), Functions.fromFunc(combineLatestFunction)); } /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ @SuppressWarnings("unchecked") public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Func4 combineLatestFunction) { return new CombineLatest(Arrays.asList(w0, w1, w2, w3), Functions.fromFunc(combineLatestFunction)); } /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ @SuppressWarnings("unchecked") public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Func5 combineLatestFunction) { return new CombineLatest(Arrays.asList(w0, w1, w2, w3, w4), Functions.fromFunc(combineLatestFunction)); } /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ @SuppressWarnings("unchecked") public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Func6 combineLatestFunction) { return new CombineLatest(Arrays.asList(w0, w1, w2, w3, w4, w5), Functions.fromFunc(combineLatestFunction)); } /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ @SuppressWarnings("unchecked") public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Observable w6, Func7 combineLatestFunction) { return new CombineLatest(Arrays.asList(w0, w1, w2, w3, w4, w5, w6), Functions.fromFunc(combineLatestFunction)); } /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ @SuppressWarnings("unchecked") public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Observable w6, Observable w7, Func8 combineLatestFunction) { return new CombineLatest(Arrays.asList(w0, w1, w2, w3, w4, w5, w6, w7), Functions.fromFunc(combineLatestFunction)); } /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ @SuppressWarnings("unchecked") public static OnSubscribeFunc combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Observable w4, Observable w5, Observable w6, Observable w7, Observable w8, Func9 combineLatestFunction) { return new CombineLatest(Arrays.asList(w0, w1, w2, w3, w4, w5, w6, w7, w8), Functions.fromFunc(combineLatestFunction)); } static final class CombineLatest implements OnSubscribeFunc { final List> sources; final FuncN combiner; public CombineLatest(Iterable> sources, FuncN combiner) { this.sources = new ArrayList>(); this.combiner = combiner; for (Observable source : sources) { this.sources.add(source); } } @Override public Subscription onSubscribe(Observer t1) { CompositeSubscription csub = new CompositeSubscription(); Collector collector = new Collector(t1, csub, sources.size()); int index = 0; List observers = new ArrayList(sources.size() + 1); for (Observable source : sources) { SafeObservableSubscription sas = new SafeObservableSubscription(); csub.add(sas); observers.add(new SourceObserver(collector, sas, index, source)); index++; } for (SourceObserver so : observers) { // if we run to completion, don't bother any further if (!csub.isUnsubscribed()) { so.connect(); } } return csub; } /** * The collector that combines the latest values from many sources. */ final class Collector { final Observer observer; final Subscription cancel; final Lock lock; final Object[] values; /** Bitmap to keep track who produced a value already. */ final BitSet hasValue; /** Bitmap to keep track who has completed. */ final BitSet completed; /** Number of source observers who have produced a value. */ int hasCount; /** Number of completed source observers. */ int completedCount; public Collector(Observer observer, Subscription cancel, int count) { this.observer = observer; this.cancel = cancel; this.values = new Object[count]; this.hasValue = new BitSet(count); this.completed = new BitSet(count); this.lock = new ReentrantLock(); } public void next(int index, T value) { Throwable err = null; lock.lock(); try { if (!isTerminated()) { values[index] = value; if (!hasValue.get(index)) { hasValue.set(index); hasCount++; } if (hasCount == values.length) { // clone: defensive copy due to varargs try { observer.onNext(combiner.call(values.clone())); } catch (Throwable t) { terminate(); err = t; } } } } finally { lock.unlock(); } if (err != null) { // no need to lock here observer.onError(err); cancel.unsubscribe(); } } public void error(int index, Throwable e) { boolean unsub = false; lock.lock(); try { if (!isTerminated()) { terminate(); unsub = true; } } finally { lock.unlock(); } if (unsub) { observer.onError(e); cancel.unsubscribe(); } } boolean isTerminated() { return completedCount == values.length + 1; } void terminate() { completedCount = values.length + 1; Arrays.fill(values, null); } public void completed(int index) { boolean unsub = false; lock.lock(); try { if (!completed.get(index)) { completed.set(index); completedCount++; } if ((!hasValue.get(index) || completedCount == values.length) && !isTerminated()) { terminate(); unsub = true; } } finally { lock.unlock(); } if (unsub) { // no need to hold a lock at this point observer.onCompleted(); cancel.unsubscribe(); } } } /** * Observes a specific source and communicates with the collector. */ final class SourceObserver implements Observer { final SafeObservableSubscription self; final Collector collector; final int index; Observable source; public SourceObserver(Collector collector, SafeObservableSubscription self, int index, Observable source) { this.self = self; this.collector = collector; this.index = index; this.source = source; } @Override public void onNext(T args) { collector.next(index, args); } @Override public void onError(Throwable e) { collector.error(index, e); } @Override public void onCompleted() { collector.completed(index); self.unsubscribe(); } /** Connect to the source. */ void connect() { self.wrap(source.subscribe(this)); source = null; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy