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

rx.internal.operators.OperatorMapNotification 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.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;

import rx.*;
import rx.Observable.Operator;
import rx.exceptions.*;
import rx.functions.*;
import rx.internal.producers.ProducerArbiter;
import rx.internal.util.unsafe.*;

/**
 * Applies a function of your choosing to every item emitted by an {@code Observable}, and emits the results of
 * this transformation as a new {@code Observable}.
 * 

* */ public final class OperatorMapNotification implements Operator { private final Func1 onNext; private final Func1 onError; private final Func0 onCompleted; public OperatorMapNotification(Func1 onNext, Func1 onError, Func0 onCompleted) { this.onNext = onNext; this.onError = onError; this.onCompleted = onCompleted; } @Override public Subscriber call(final Subscriber o) { final ProducerArbiter pa = new ProducerArbiter(); MapNotificationSubscriber subscriber = new MapNotificationSubscriber(pa, o); o.add(subscriber); subscriber.init(); return subscriber; } final class MapNotificationSubscriber extends Subscriber { private final Subscriber o; private final ProducerArbiter pa; final SingleEmitter emitter; private MapNotificationSubscriber(ProducerArbiter pa, Subscriber o) { this.pa = pa; this.o = o; this.emitter = new SingleEmitter(o, pa, this); } void init() { o.setProducer(emitter); } @Override public void setProducer(Producer producer) { pa.setProducer(producer); } @Override public void onCompleted() { try { emitter.offerAndComplete(onCompleted.call()); } catch (Throwable e) { Exceptions.throwOrReport(e, o); } } @Override public void onError(Throwable e) { try { emitter.offerAndComplete(onError.call(e)); } catch (Throwable e2) { Exceptions.throwOrReport(e2, o); } } @Override public void onNext(T t) { try { emitter.offer(onNext.call(t)); } catch (Throwable e) { Exceptions.throwOrReport(e, o, t); } } } static final class SingleEmitter extends AtomicLong implements Producer, Subscription { /** */ private static final long serialVersionUID = -249869671366010660L; final NotificationLite nl; final Subscriber child; final Producer producer; final Subscription cancel; final Queue queue; volatile boolean complete; /** Guarded by this. */ boolean emitting; /** Guarded by this. */ boolean missed; public SingleEmitter(Subscriber child, Producer producer, Subscription cancel) { this.child = child; this.producer = producer; this.cancel = cancel; this.queue = UnsafeAccess.isUnsafeAvailable() ? new SpscArrayQueue(2) : new ConcurrentLinkedQueue(); this.nl = NotificationLite.instance(); } @Override public void request(long n) { for (;;) { long r = get(); if (r < 0) { return; } long u = r + n; if (u < 0) { u = Long.MAX_VALUE; } if (compareAndSet(r, u)) { producer.request(n); drain(); return; } } } void produced(long n) { for (;;) { long r = get(); if (r < 0) { return; } long u = r - n; if (u < 0) { throw new IllegalStateException("More produced (" + n + ") than requested (" + r + ")"); } if (compareAndSet(r, u)) { return; } } } public void offer(T value) { if (!queue.offer(value)) { child.onError(new MissingBackpressureException()); unsubscribe(); } else { drain(); } } public void offerAndComplete(T value) { if (!this.queue.offer(value)) { child.onError(new MissingBackpressureException()); unsubscribe(); } else { this.complete = true; drain(); } } void drain() { synchronized (this) { if (emitting) { missed = true; return; } emitting = true; missed = false; } boolean skipFinal = false; try { for (;;) { long r = get(); boolean c = complete; boolean empty = queue.isEmpty(); if (c && empty) { child.onCompleted(); skipFinal = true; return; } else if (r > 0) { Object v = queue.poll(); if (v != null) { child.onNext(nl.getValue(v)); produced(1); } else if (c) { child.onCompleted(); skipFinal = true; return; } } synchronized (this) { if (!missed) { skipFinal = true; emitting = false; return; } missed = false; } } } finally { if (!skipFinal) { synchronized (this) { emitting = false; } } } } @Override public boolean isUnsubscribed() { return get() < 0; } @Override public void unsubscribe() { long r = get(); if (r != Long.MIN_VALUE) { r = getAndSet(Long.MIN_VALUE); if (r != Long.MIN_VALUE) { cancel.unsubscribe(); } } } } }