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

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

There is a newer version: 1.3.8
Show newest version
/**
 * Copyright 2016 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 java.util.concurrent.atomic.*;

import rx.*;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.exceptions.*;
import rx.functions.Func1;
import rx.internal.util.*;
import rx.internal.util.atomic.*;
import rx.internal.util.unsafe.*;
import rx.plugins.RxJavaHooks;

/**
 * Flattens a sequence if Iterable sources, generated via a function, into a single sequence.
 *
 * @param  the input value type
 * @param  the output value type
 */
public final class OnSubscribeFlattenIterable implements OnSubscribe {
    
    final Observable source;
    
    final Func1> mapper;
    
    final int prefetch;
    
    /** Protected: use createFrom to handle source-dependent optimizations. */
    protected OnSubscribeFlattenIterable(Observable source,
            Func1> mapper, int prefetch) {
        this.source = source;
        this.mapper = mapper;
        this.prefetch = prefetch;
    }
    
    @Override
    public void call(Subscriber t) {
        final FlattenIterableSubscriber parent = new FlattenIterableSubscriber(t, mapper, prefetch);
        
        t.add(parent);
        t.setProducer(new Producer() {
            @Override
            public void request(long n) {
                parent.requestMore(n);
            }
        });
        
        source.unsafeSubscribe(parent);
    }
    
    public static  Observable createFrom(Observable source,
            Func1> mapper, int prefetch) {
        if (source instanceof ScalarSynchronousObservable) {
            T scalar = ((ScalarSynchronousObservable) source).get();
            return Observable.create(new OnSubscribeScalarFlattenIterable(scalar, mapper));
        }
        return Observable.create(new OnSubscribeFlattenIterable(source, mapper, prefetch));
    }
    
    static final class FlattenIterableSubscriber extends Subscriber {
        final Subscriber actual;
        
        final Func1> mapper;
        
        final long limit;
        
        final Queue queue;

        final AtomicReference error;
        
        final AtomicLong requested;
        
        final AtomicInteger wip;
        
        final NotificationLite nl;
        
        volatile boolean done;
        
        long produced;
        
        Iterator active;
        
        public FlattenIterableSubscriber(Subscriber actual,
                Func1> mapper, int prefetch) {
            this.actual = actual;
            this.mapper = mapper;
            this.error = new AtomicReference();
            this.wip = new AtomicInteger();
            this.requested = new AtomicLong();
            this.nl = NotificationLite.instance();
            if (prefetch == Integer.MAX_VALUE) {
                this.limit = Long.MAX_VALUE;
                this.queue = new SpscLinkedArrayQueue(RxRingBuffer.SIZE);
            } else {
                // limit = prefetch * 75% rounded up
                this.limit = prefetch - (prefetch >> 2);
                if (UnsafeAccess.isUnsafeAvailable()) {
                    this.queue = new SpscArrayQueue(prefetch);
                } else {
                    this.queue = new SpscAtomicArrayQueue(prefetch);
                }
            }
            request(prefetch);
        }
        
        @Override
        public void onNext(T t) {
            if (!queue.offer(nl.next(t))) {
                unsubscribe();
                onError(new MissingBackpressureException());
                return;
            }
            drain();
        }
        
        @Override
        public void onError(Throwable e) {
            if (ExceptionsUtils.addThrowable(error, e)) {
                done = true;
                drain();
            } else {
                RxJavaHooks.onError(e);
            }
        }
        
        @Override
        public void onCompleted() {
            done = true;
            drain();
        }
        
        void requestMore(long n) {
            if (n > 0) {
                BackpressureUtils.getAndAddRequest(requested, n);
                drain();
            } else if (n < 0) {
                throw new IllegalStateException("n >= 0 required but it was " + n);
            }
        }
        
        void drain() {
            if (wip.getAndIncrement() != 0) {
                return;
            }
            
            final Subscriber actual = this.actual;
            final Queue queue = this.queue;
            
            int missed = 1;
            
            for (;;) {
                
                Iterator it = active;
                
                if (it == null) {
                    boolean d = done;
                    
                    Object v = queue.poll();
                    
                    boolean empty = v == null;

                    if (checkTerminated(d, empty, actual, queue)) {
                        return;
                    }
                    
                    if (!empty) {
                    
                        long p = produced + 1;
                        if (p == limit) {
                            produced = 0L;
                            request(p);
                        } else {
                            produced = p;
                        }
                        
                        boolean b;
                        
                        try {
                            Iterable iter = mapper.call(nl.getValue(v));
                            
                            it = iter.iterator();
                            
                            b = it.hasNext();
                        } catch (Throwable ex) {
                            Exceptions.throwIfFatal(ex);
                            
                            it = null;
                            onError(ex);
                            
                            continue;
                        }
                        
                        if (!b) {
                            continue;
                        }
                        
                        active = it;
                    }
                }
                
                if (it != null) {
                    long r = requested.get();
                    long e = 0L;
                    
                    while (e != r) {
                        if (checkTerminated(done, false, actual, queue)) {
                            return;
                        }
                        
                        R v;
                        
                        try {
                            v = it.next();
                        } catch (Throwable ex) {
                            Exceptions.throwIfFatal(ex);
                            it = null;
                            active = null;
                            onError(ex);
                            break;
                        }
                        
                        actual.onNext(v);

                        if (checkTerminated(done, false, actual, queue)) {
                            return;
                        }

                        e++;

                        boolean b;
                        
                        try {
                            b = it.hasNext();
                        } catch (Throwable ex) {
                            Exceptions.throwIfFatal(ex);
                            it = null;
                            active = null;
                            onError(ex);
                            break;
                        }
                        
                        if (!b) {
                            it = null;
                            active = null;
                            break;
                        }
                    }
                    
                    if (e == r) {
                        if (checkTerminated(done, queue.isEmpty() && it == null, actual, queue)) {
                            return;
                        }
                    }
                    
                    if (e != 0L) {
                        BackpressureUtils.produced(requested, e);
                    }
                    
                    if (it == null) {
                        continue;
                    }
                }
                
                missed = wip.addAndGet(-missed);
                if (missed == 0) {
                    break;
                }
            }
        }
        
        boolean checkTerminated(boolean d, boolean empty, Subscriber a, Queue q) {
            if (a.isUnsubscribed()) {
                q.clear();
                active = null;
                return true;
            }
            
            if (d) {
                Throwable ex = error.get();
                if (ex != null) {
                    ex = ExceptionsUtils.terminate(error);
                    unsubscribe();
                    q.clear();
                    active = null;
                    
                    a.onError(ex);
                    return true;
                } else
                if (empty) {
                    
                    a.onCompleted();
                    return true;
                }
            }
            
            return false;
        }
    }
    
    /**
     * A custom flattener that works from a scalar value and computes the iterable
     * during subscription time.
     *
     * @param  the scalar's value type
     * @param  the result value type
     */
    static final class OnSubscribeScalarFlattenIterable implements OnSubscribe {
        final T value;
        
        final Func1> mapper;

        public OnSubscribeScalarFlattenIterable(T value, Func1> mapper) {
            this.value = value;
            this.mapper = mapper;
        }

        @Override
        public void call(Subscriber t) {
            Iterator itor;
            boolean b;
            try {
                Iterable it = mapper.call(value);
                
                itor = it.iterator();
                
                b = itor.hasNext();
            } catch (Throwable ex) {
                Exceptions.throwOrReport(ex, t, value);
                return;
            }
            
            if (!b) {
                t.onCompleted();
                return;
            }
            
            t.setProducer(new OnSubscribeFromIterable.IterableProducer(t, itor));
        }
    }
}