Package rx.internal.operators

Source Code of rx.internal.operators.OperatorGroupBy$GroupBySubscriber

/**
  * 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.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

import rx.Observable.OnSubscribe;
import rx.Observable.Operator;
import rx.Subscriber;
import rx.exceptions.OnErrorThrowable;
import rx.functions.Action0;
import rx.functions.Func1;
import rx.observables.GroupedObservable;
import rx.subscriptions.Subscriptions;

/**
* Groups the items emitted by an Observable according to a specified criterion, and emits these
* grouped items as Observables, one Observable per group.
* <p>
* <img width="640" height="360" src="https://raw.githubusercontent.com/wiki/Netflix/RxJava/images/rx-operators/groupBy.png" alt="">
*
* @param <K> the key type
* @param <T> the source and group value type
*/
public final class OperatorGroupBy<K, T> implements Operator<GroupedObservable<K, T>, T> {
   
    final Func1<? super T, ? extends K> keySelector;
   
    public OperatorGroupBy(final Func1<? super T, ? extends K> keySelector) {
        this.keySelector = keySelector;
    }
   
    @Override
    public Subscriber<? super T> call(final Subscriber<? super GroupedObservable<K, T>> child) {
        return new GroupBySubscriber<K, T>(keySelector, child);
    }
    static final class GroupBySubscriber<K, T> extends Subscriber<T> {
        final Func1<? super T, ? extends K> keySelector;
        final Subscriber<? super GroupedObservable<K, T>> child;
        public GroupBySubscriber(Func1<? super T, ? extends K> keySelector, Subscriber<? super GroupedObservable<K, T>> child) {
            // a new CompositeSubscription to decouple the subscription as the inner subscriptions need a separate lifecycle
            // and will unsubscribe on this parent if they are all unsubscribed
            super();
            this.keySelector = keySelector;
            this.child = child;
        }
        private final Map<K, BufferUntilSubscriber<T>> groups = new HashMap<K, BufferUntilSubscriber<T>>();
        volatile int completionCounter;
        volatile int completionEmitted;
        volatile int terminated;
       
        @SuppressWarnings("rawtypes")
        static final AtomicIntegerFieldUpdater<GroupBySubscriber> COUNTER_UPDATER
                = AtomicIntegerFieldUpdater.newUpdater(GroupBySubscriber.class, "completionCounter");
        @SuppressWarnings("rawtypes")
        static final AtomicIntegerFieldUpdater<GroupBySubscriber> EMITTED_UPDATER
                = AtomicIntegerFieldUpdater.newUpdater(GroupBySubscriber.class, "completionEmitted");
        @SuppressWarnings("rawtypes")
        static final AtomicIntegerFieldUpdater<GroupBySubscriber> TERMINATED_UPDATER
                = AtomicIntegerFieldUpdater.newUpdater(GroupBySubscriber.class, "terminated");
       
        @Override
        public void onStart() {
            /*
             * This operator does not support backpressure as splitting a stream effectively turns it into a "hot observable" and
             * blocking any one group would block the entire parent stream. If backpressure is needed on individual groups then
             * operators such as `onBackpressureDrop` or `onBackpressureBuffer` should be used.
             */
            request(Long.MAX_VALUE);
        }
       
        @Override
        public void onCompleted() {
            if (TERMINATED_UPDATER.compareAndSet(this, 0, 1)) {
                // if we receive onCompleted from our parent we onComplete children
                for (BufferUntilSubscriber<T> ps : groups.values()) {
                    ps.onCompleted();
                }
               
                // special case for empty (no groups emitted)
                if (completionCounter == 0) {
                    // we must track 'completionEmitted' seperately from 'completed' since `completeInner` can result in childObserver.onCompleted() being emitted
                    if (EMITTED_UPDATER.compareAndSet(this, 0, 1)) {
                        child.onCompleted();
                    }
                }
            }
        }
       
        @Override
        public void onError(Throwable e) {
            if (TERMINATED_UPDATER.compareAndSet(this, 0, 1)) {
                // we immediately tear everything down if we receive an error
                child.onError(e);
            }
        }
       
        @Override
        public void onNext(T t) {
            try {
                final K key = keySelector.call(t);
                BufferUntilSubscriber<T> group = groups.get(key);
                if (group == null) {
                    // this group doesn't exist
                    if (child.isUnsubscribed()) {
                        // we have been unsubscribed on the outer so won't send any  more groups
                        return;
                    }
                    group = BufferUntilSubscriber.create();
                    final BufferUntilSubscriber<T> _group = group;
                   
                    GroupedObservable<K, T> go = new GroupedObservable<K, T>(key, new OnSubscribe<T>() {
                       
                        @Override
                        public void call(final Subscriber<? super T> o) {
                            // number of children we have running
                            COUNTER_UPDATER.incrementAndGet(GroupBySubscriber.this);
                            o.add(Subscriptions.create(new Action0() {
                               
                                @Override
                                public void call() {
                                    completeInner();
                                }
                               
                            }));
                            _group.unsafeSubscribe(new Subscriber<T>(o) {
                               
                                @Override
                                public void onCompleted() {
                                    o.onCompleted();
                                    completeInner();
                                }
                               
                                @Override
                                public void onError(Throwable e) {
                                    o.onError(e);
                                }
                               
                                @Override
                                public void onNext(T t) {
                                    o.onNext(t);
                                }
                               
                            });
                        }
                       
                    });
                    groups.put(key, group);
                    child.onNext(go);
                }
                // we have the correct group so send value to it
                group.onNext(t);
            } catch (Throwable e) {
                onError(OnErrorThrowable.addValueAsLastCause(e, t));
            }
        }
       
        private void completeInner() {
            // count can be < 0 because unsubscribe also calls this
            if (COUNTER_UPDATER.decrementAndGet(this) <= 0 && (terminated == 1 || child.isUnsubscribed())) {
                // completionEmitted ensures we only emit onCompleted once
                if (EMITTED_UPDATER.compareAndSet(this, 0, 1)) {
                    if (child.isUnsubscribed()) {
                        // if the entire groupBy has been unsubscribed and children are completed we will propagate the unsubscribe up.
                        unsubscribe();
                    }
                    child.onCompleted();
                }
            }
        }
       
    }
}
TOP

Related Classes of rx.internal.operators.OperatorGroupBy$GroupBySubscriber

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.