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

rx.internal.operators.OperatorMergeMapPair Maven / Gradle / Ivy

 /**
  * 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.internal.operators;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import rx.Observable;
import rx.Observable.Operator;
import rx.Subscriber;
import rx.functions.Func1;
import rx.functions.Func2;
import rx.observers.SerializedSubscriber;
import rx.subscriptions.CompositeSubscription;

/**
 * An {@link Operator} that pairs up items emitted by a source {@link Observable} with the sequence of items
 * emitted by the {@code Observable} that is derived from each item by means of a selector, and emits the
 * results of this pairing.
 *
 * @param  the type of items emitted by the source {@code Observable}
 * @param  the type of items emitted by the derived {@code Observable}s
 * @param  the type of items to be emitted by this {@code Operator}
 */
public final class OperatorMergeMapPair implements Operator {

    /**
     * Creates the function that generates a {@code Observable} based on an item emitted by another
     * {@code Observable}.
     * 
     * @param selector
     *          a function that accepts an item and returns an {@code Iterable} of corresponding items
     * @return a function that converts an item emitted by the source {@code Observable} into an
     *         {@code Observable} that emits the items generated by {@code selector} operating on that item
     */
    public static  Func1> convertSelector(final Func1> selector) {
        return new Func1>() {
            @Override
            public Observable call(T t1) {
                return Observable.from(selector.call(t1));
            }
        };
    }
    
    final Func1> collectionSelector;
    final Func2 resultSelector;

    public OperatorMergeMapPair(Func1> collectionSelector,
            Func2 resultSelector) {
        this.collectionSelector = collectionSelector;
        this.resultSelector = resultSelector;
    }
    
    @Override
    public Subscriber call(Subscriber child) {
        final SerializedSubscriber s = new SerializedSubscriber(child);
        final CompositeSubscription csub = new CompositeSubscription();
        child.add(csub);
     
        return new SourceSubscriber(s, csub, collectionSelector, resultSelector);
    }

    static final class SourceSubscriber extends Subscriber {
        final Subscriber s;
        final CompositeSubscription csub;
        final Func1> collectionSelector;
        final Func2 resultSelector;

        volatile int wip;
        @SuppressWarnings("rawtypes")
        static final AtomicIntegerFieldUpdater WIP_UPDATER
                = AtomicIntegerFieldUpdater.newUpdater(SourceSubscriber.class, "wip");

        public SourceSubscriber(Subscriber s, CompositeSubscription csub, 
                Func1> collectionSelector, 
                Func2 resultSelector) {
            super(s);
            this.s = s;
            this.csub = csub;
            this.collectionSelector = collectionSelector;
            this.resultSelector = resultSelector;
            this.wip = 1;
        }
        
        @Override
        public void onNext(final T t) {
            Observable collection;
            try {
                collection = collectionSelector.call(t);
            } catch (Throwable e) {
                onError(e);
                return;
            }
            
            Subscriber collectionSub = new Subscriber() {
                
                @Override
                public void onNext(U u) {
                    try {
                        s.onNext(resultSelector.call(t, u));
                    } catch (Throwable e) {
                        onError(e);
                    }
                }
                
                @Override
                public void onError(Throwable e) {
                    SourceSubscriber.this.onError(e);
                }
                
                @Override
                public void onCompleted() {
                    try {
                        SourceSubscriber.this.onCompleted();
                    } finally {
                        csub.remove(this);
                    }
                }
            };
            csub.add(collectionSub);
            WIP_UPDATER.incrementAndGet(this);
            
            collection.unsafeSubscribe(collectionSub);
        }
        
        @Override
        public void onError(Throwable e) {
            s.onError(e);
            unsubscribe();
        }
        
        @Override
        public void onCompleted() {
            if (WIP_UPDATER.decrementAndGet(this) == 0) {
                s.onCompleted();
            }
        }
        
    }
}