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

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

There is a newer version: 1.3.8
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.internal.operators;

import java.util.*;

import rx.*;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Observer;
import rx.exceptions.Exceptions;
import rx.functions.*;
import rx.observers.*;
import rx.subjects.*;
import rx.subscriptions.*;

/**
 * Correlates two sequences when they overlap and groups the results.
 * 
 * @see MSDN: Observable.GroupJoin
 * @param  the left value type
 * @param  the right value type
 * @param  the value type of the left duration
 * @param  the value type of the right duration
 * @param  the result value type
 */
public final class OnSubscribeGroupJoin implements OnSubscribe {
    final Observable left;
    final Observable right;
    final Func1> leftDuration;
    final Func1> rightDuration;
    final Func2, ? extends R> resultSelector;

    public OnSubscribeGroupJoin(
            Observable left,
            Observable right,
            Func1> leftDuration,
            Func1> rightDuration,
            Func2, ? extends R> resultSelector) {
        this.left = left;
        this.right = right;
        this.leftDuration = leftDuration;
        this.rightDuration = rightDuration;
        this.resultSelector = resultSelector;
    }

    @Override
    public void call(Subscriber child) {
        ResultManager ro = new ResultManager(new SerializedSubscriber(child));
        child.add(ro);
        ro.init();
    }

    /** Manages sub-observers and subscriptions. */
    final class ResultManager extends HashMap>implements Subscription {
        // HashMap aspect of `this` refers to `leftMap`
        
        private static final long serialVersionUID = -3035156013812425335L;
        
        final RefCountSubscription cancel;
        final Subscriber subscriber;
        final CompositeSubscription group;
        /** Guarded by this. */
        int leftIds;
        /** Guarded by this. */
        int rightIds;
        /** Guarded by this. */
        final Map rightMap = new HashMap(); // NOPMD 
        /** Guarded by this. */
        boolean leftDone;
        /** Guarded by this. */
        boolean rightDone;

        public ResultManager(Subscriber subscriber) {
            super();
            this.subscriber = subscriber;
            this.group = new CompositeSubscription();
            this.cancel = new RefCountSubscription(group);
        }

        public void init() {

            Subscriber s1 = new LeftObserver();
            Subscriber s2 = new RightObserver();
            
            group.add(s1);
            group.add(s2);

            left.unsafeSubscribe(s1);
            right.unsafeSubscribe(s2);
        }

        @Override
        public void unsubscribe() {
            cancel.unsubscribe();
        }
        
        @Override
        public boolean isUnsubscribed() {
            return cancel.isUnsubscribed();
        }
        
        Map> leftMap() {
            return this;
        }
        
        /**
         * Notify everyone and cleanup.
         * @param e the exception
         */
        void errorAll(Throwable e) {
            List> list;
            synchronized (ResultManager.this) {
                list = new ArrayList>(leftMap().values());
                leftMap().clear();
                rightMap.clear();
            }
            for (Observer o : list) {
                o.onError(e);
            }
            subscriber.onError(e);
            cancel.unsubscribe();
        }
        /**
         * Notify only the main subscriber and cleanup.
         * @param e  the exception
         */
        void errorMain(Throwable e) {
            synchronized (ResultManager.this) {
                leftMap().clear();
                rightMap.clear();
            }            
            subscriber.onError(e);
            cancel.unsubscribe();
        }
        void complete(List> list) {
            if (list != null) {
                for (Observer o : list) {
                    o.onCompleted();
                }
                subscriber.onCompleted();
                cancel.unsubscribe();
            }
        }
        
        /** Observe the left source. */
        final class LeftObserver extends Subscriber {
            @Override
            public void onNext(T1 args) {
                try {
                    int id;
                    Subject subj = PublishSubject.create();
                    Observer subjSerial = new SerializedObserver(subj);
                    
                    synchronized (ResultManager.this) {
                        id = leftIds++;
                        leftMap().put(id, subjSerial);
                    }

                    Observable window = Observable.create(new WindowObservableFunc(subj, cancel));

                    Observable duration = leftDuration.call(args);

                    Subscriber d1 = new LeftDurationObserver(id);
                    group.add(d1);
                    duration.unsafeSubscribe(d1);

                    R result = resultSelector.call(args, window);

                    List rightMapValues;
                    synchronized (ResultManager.this) {
                        rightMapValues = new ArrayList(rightMap.values());
                    }
                    
                    subscriber.onNext(result);
                    for (T2 t2 : rightMapValues) {
                        subjSerial.onNext(t2);
                    }
                    
                    
                } catch (Throwable t) {
                    Exceptions.throwOrReport(t, this);
                }
            }

            @Override
            public void onCompleted() {
                List> list = null;
                synchronized (ResultManager.this) {
                    leftDone = true;
                    if (rightDone) {
                        list = new ArrayList>(leftMap().values());
                        leftMap().clear();
                        rightMap.clear();
                    }
                }
                complete(list);
            }

            @Override
            public void onError(Throwable e) {
                errorAll(e);
            }

        }

        /** Observe the right source. */
        final class RightObserver extends Subscriber {
            @Override
            public void onNext(T2 args) {
                try {
                    int id;
                    synchronized (ResultManager.this) {
                        id = rightIds++;
                        rightMap.put(id, args);
                    }
                    Observable duration = rightDuration.call(args);

                    Subscriber d2 = new RightDurationObserver(id);
                    
                    group.add(d2);
                    duration.unsafeSubscribe(d2);

                    List> list;
                    synchronized (ResultManager.this) {
                        list = new ArrayList>(leftMap().values());
                    }
                    for (Observer o : list) {
                        o.onNext(args);
                    }
                } catch (Throwable t) {
                    Exceptions.throwOrReport(t, this);
                }
            }

            @Override
            public void onCompleted() {
                List> list = null;
                synchronized (ResultManager.this) {
                    rightDone = true;
                    if (leftDone) {
                        list = new ArrayList>(leftMap().values());
                        leftMap().clear();
                        rightMap.clear();
                    }
                }
                complete(list);
            }

            @Override
            public void onError(Throwable e) {
                errorAll(e);
            }
        }

        /** Observe left duration and apply termination. */
        final class LeftDurationObserver extends Subscriber {
            final int id;
            boolean once = true;

            public LeftDurationObserver(int id) {
                this.id = id;
            }

            @Override
            public void onCompleted() {
                if (once) {
                    once = false;
                    Observer gr;
                    synchronized (ResultManager.this) {
                        gr = leftMap().remove(id);
                    }
                    if (gr != null) {
                        gr.onCompleted();
                    }
                    group.remove(this);
                }
            }

            @Override
            public void onError(Throwable e) {
                errorMain(e);
            }

            @Override
            public void onNext(D1 args) {
                onCompleted();
            }
        }

        /** Observe right duration and apply termination. */
        final class RightDurationObserver extends Subscriber {
            final int id;
            boolean once = true;
            public RightDurationObserver(int id) {
                this.id = id;
            }

            @Override
            public void onCompleted() {
                if (once) {
                    once = false;
                    synchronized (ResultManager.this) {
                        rightMap.remove(id);
                    }
                    group.remove(this);
                }
            }

            @Override
            public void onError(Throwable e) {
                errorMain(e);
            }

            @Override
            public void onNext(D2 args) {
                onCompleted();
            }
        }

    }

    /**
     * The reference-counted window observable.
     * Subscribes to the underlying Observable by using a reference-counted
     * subscription.
     */
    final static class WindowObservableFunc implements OnSubscribe {
        final RefCountSubscription refCount;
        final Observable underlying;

        public WindowObservableFunc(Observable underlying, RefCountSubscription refCount) {
            this.refCount = refCount;
            this.underlying = underlying;
        }

        @Override
        public void call(Subscriber t1) {
            Subscription ref = refCount.get();
            WindowSubscriber wo = new WindowSubscriber(t1, ref);
            wo.add(ref);
            
            underlying.unsafeSubscribe(wo);
        }

        /** Observe activities on the window. */
        final class WindowSubscriber extends Subscriber {
            final Subscriber subscriber;
            private final Subscription ref;

            public WindowSubscriber(Subscriber subscriber, Subscription ref) {
                super(subscriber);
                this.subscriber = subscriber;
                this.ref = ref;
            }

            @Override
            public void onNext(T args) {
                subscriber.onNext(args);
            }

            @Override
            public void onError(Throwable e) {
                subscriber.onError(e);
                ref.unsubscribe();
            }

            @Override
            public void onCompleted() {
                subscriber.onCompleted();
                ref.unsubscribe();
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy