/* * Copyright 2015 Jacek Marchwicki <jacek.marchwicki@gmail.com> * * 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 com.appunite.rx.subjects; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; import rx.Observable; import rx.Subscriber; import rx.functions.Action0; import rx.functions.Func0; import rx.internal.operators.OperatorMulticast; import rx.observables.ConnectableObservable; import rx.subjects.Subject; import rx.subscriptions.Subscriptions; public class CacheSubject<T> extends Subject<T, T> { @Nonnull private final CacheCreator<T> cacheCreator; @Nonnull private final List<Subscriber<? super T>> subscribers = new ArrayList<>(); private final boolean skipNextNull; @Nonnull public static <T> CacheSubject<T> create(@Nonnull CacheCreator<T> cacheCreator) { return create(cacheCreator, true); } @Nonnull public static <T> CacheSubject<T> create(@Nonnull CacheCreator<T> cacheCreator, final boolean skipFirstNull) { return create(cacheCreator, skipFirstNull, true); } @Nonnull public static <T> CacheSubject<T> create(@Nonnull CacheCreator<T> cacheCreator, final boolean skipFirstNull, final boolean skipNextNull) { return new CacheSubject<>(cacheCreator, new DelegateOnSubscribe<T>(), skipFirstNull, skipNextNull); } @Nonnull public static <T> ConnectableObservable<T> behavior(final @Nonnull Observable<T> observable, final @Nonnull CacheSubject.CacheCreator<T> cacheCreator) { return new OperatorMulticast<>(observable, new Func0<Subject<? super T, ? extends T>>() { @Override public Subject<? super T, ? extends T> call() { return CacheSubject.create(cacheCreator); } }); } @Nonnull public static <T> Observable.Transformer<T, T> behaviorRefCount( final @Nonnull CacheSubject.CacheCreator<T> cacheCreator) { return new Observable.Transformer<T, T>() { @Override public Observable<T> call(final Observable<T> tObservable) { return behavior(tObservable, cacheCreator).refCount(); } }; } private CacheSubject(@Nonnull final CacheCreator<T> cacheCreator, @Nonnull DelegateOnSubscribe<T> delegateOnSubscribe, final boolean skipFirstNull, final boolean skipNextNull) { super(delegateOnSubscribe); this.skipNextNull = skipNextNull; delegateOnSubscribe.setDelegate(new OnSubscribe<T>() { @Override public void call(final Subscriber<? super T> child) { final T t = cacheCreator.readFromCache(); if (!skipFirstNull || t != null) { child.onNext(t); } add(child); child.add(Subscriptions.create(new Action0() { @Override public void call() { remove(child); } })); } }); this.cacheCreator = cacheCreator; } private void add(Subscriber<? super T> child) { synchronized (subscribers) { subscribers.add(child); } } private void remove(Subscriber<? super T> child) { synchronized (subscribers) { subscribers.remove(child); } } @Override public boolean hasObservers() { return !getSubscribers().isEmpty(); } @Override public void onCompleted() { final List<Subscriber<? super T>> subscribers = getSubscribers(); for (Subscriber<? super T> subscriber : subscribers) { subscriber.onCompleted(); } } @Override public void onError(Throwable e) { final List<Subscriber<? super T>> subscribers = getSubscribers(); for (Subscriber<? super T> subscriber : subscribers) { subscriber.onError(e); } } /* * This is very dump solution not efficient but is straight forward so it is cool */ private List<Subscriber<? super T>> getSubscribers() { synchronized (subscribers) { return new ArrayList<>(subscribers); } } @Override public void onNext(T t) { if (!skipNextNull || t != null) { final List<Subscriber<? super T>> subscribers = getSubscribers(); for (Subscriber<? super T> subscriber : subscribers) { subscriber.onNext(t); } } cacheCreator.writeToCache(t); } public interface CacheCreator<T> { @Nullable T readFromCache(); void writeToCache(@Nullable T data); } public static class InMemoryCache<T> implements CacheCreator<T> { @Nullable private T cache; public InMemoryCache(@Nullable T cache) { this.cache = cache; } @Nullable @Override public T readFromCache() { return cache; } @Override public void writeToCache(@Nullable T data) { cache = data; } } private static class DelegateOnSubscribe<T> implements OnSubscribe<T> { private OnSubscribe<T> delegate = null; @Override public void call(Subscriber<? super T> subscriber) { delegate.call(subscriber); } public void setDelegate(OnSubscribe<T> delegate) { this.delegate = delegate; } } }