/** * 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.io.Serializable; import rx.Notification.Kind; import rx.Observer; /** * For use in internal operators that need something like materialize and dematerialize wholly within the * implementation of the operator but don't want to incur the allocation cost of actually creating * {@link rx.Notification} objects for every {@link Observer#onNext onNext} and * {@link Observer#onCompleted onCompleted}. * <p> * An object is allocated inside {@link #error(Throwable)} to wrap the {@link Throwable} but this shouldn't * affect performance because exceptions should be exceptionally rare. * <p> * It's implemented as a singleton to maintain some semblance of type safety that is completely non-existent. * * @param <T> * @warn type param undescribed */ public final class NotificationLite<T> { private NotificationLite() { } @SuppressWarnings("rawtypes") private static final NotificationLite INSTANCE = new NotificationLite(); /** * Gets the {@code NotificationLite} singleton. * * @return the sole {@code NotificationLite} object */ @SuppressWarnings("unchecked") public static <T> NotificationLite<T> instance() { return INSTANCE; } private static final Object ON_COMPLETED_SENTINEL = new Serializable() { private static final long serialVersionUID = 1; public String toString() { return "Notification=>Completed"; } }; private static final Object ON_NEXT_NULL_SENTINEL = new Serializable() { private static final long serialVersionUID = 2; public String toString() { return "Notification=>NULL"; } }; private static class OnErrorSentinel implements Serializable { private static final long serialVersionUID = 3; private final Throwable e; public OnErrorSentinel(Throwable e) { this.e = e; } public String toString() { return "Notification=>Error:" + e.getMessage(); } } /** * Creates a lite {@code onNext} notification for the value passed in without doing any allocation. Can * be unwrapped and sent with the {@link #accept} method. * * @param t * the item emitted to {@code onNext} * @return the item, or a null token representing the item if the item is {@code null} */ public Object next(T t) { if (t == null) return ON_NEXT_NULL_SENTINEL; else return t; } /** * Creates a lite {@code onCompleted} notification without doing any allocation. Can be unwrapped and * sent with the {@link #accept} method. * * @return a completion token */ public Object completed() { return ON_COMPLETED_SENTINEL; } /** * Create a lite {@code onError} notification. This call creates an object to wrap the {@link Throwable}, * but since there should only be one of these, the performance impact should be small. Can be unwrapped and * sent with the {@link #accept} method. * * @param e * the {@code Throwable} in the {@code onError} notification * @return an object encapsulating the exception */ public Object error(Throwable e) { return new OnErrorSentinel(e); } /** * Unwraps the lite notification and calls the appropriate method on the {@link Observer}. * * @param o * the {@link Observer} to call {@code onNext}, {@code onCompleted}, or {@code onError}. * @param n * the lite notification * @return {@code true} if {@code n} represents a termination event; {@code false} otherwise * @throws IllegalArgumentException * if the notification is null. * @throws NullPointerException * if the {@link Observer} is null. */ @SuppressWarnings("unchecked") public boolean accept(Observer<? super T> o, Object n) { if (n == ON_COMPLETED_SENTINEL) { o.onCompleted(); return true; } else if (n == ON_NEXT_NULL_SENTINEL) { o.onNext(null); return false; } else if (n != null) { if (n.getClass() == OnErrorSentinel.class) { o.onError(((OnErrorSentinel) n).e); return true; } o.onNext((T) n); return false; } else { throw new IllegalArgumentException("The lite notification can not be null"); } } /** * Indicates whether or not the lite notification represents an {@code onCompleted} event. * * @param n * the lite notification * @return {@code true} if {@code n} represents an {@code onCompleted} event; {@code false} otherwise */ public boolean isCompleted(Object n) { return n == ON_COMPLETED_SENTINEL; } /** * Indicates whether or not the lite notification represents an {@code onError} event. * * @param n * the lite notification * @return {@code true} if {@code n} represents an {@code onError} event; {@code false} otherwise */ public boolean isError(Object n) { return n instanceof OnErrorSentinel; } /** * Indicates which variety a particular lite notification is. If you need something more complex than * simply calling the right method on an {@link Observer} then you can use this method to get the * {@link rx.Notification.Kind}. * * @param n * the lite notification * @throws IllegalArgumentException * if the notification is null. * @return the {@link Kind} of lite notification {@code n} is: either {@code Kind.OnCompleted}, * {@code Kind.OnError}, or {@code Kind.OnNext} */ public Kind kind(Object n) { if (n == null) throw new IllegalArgumentException("The lite notification can not be null"); else if (n == ON_COMPLETED_SENTINEL) return Kind.OnCompleted; else if (n instanceof OnErrorSentinel) return Kind.OnError; else // value or ON_NEXT_NULL_SENTINEL but either way it's an OnNext return Kind.OnNext; } /** * Returns the item corresponding to this {@code OnNext} lite notification. Bad things happen if you pass * this an {@code OnComplete} or {@code OnError} notification type. For performance reasons, this method * does not check for this, so you are expected to prevent such a mishap. * * @param n * the lite notification (of type {@code Kind.OnNext}) * @return the unwrapped value, which can be null */ @SuppressWarnings("unchecked") public T getValue(Object n) { return n == ON_NEXT_NULL_SENTINEL ? null : (T) n; } /** * Returns the {@link Throwable} corresponding to this {@code OnError} lite notification. Bad things happen * if you pass this an {@code OnComplete} or {@code OnNext} notification type. For performance reasons, this * method does not check for this, so you are expected to prevent such a mishap. * * @param n * the lite notification (of type {@code Kind.OnError}) * @return the {@link Throwable} wrapped inside {@code n} */ public Throwable getError(Object n) { return ((OnErrorSentinel) n).e; } }