diff --git a/rxjava-core/build.gradle b/rxjava-core/build.gradle index bcb1707d6d..03d0e79e94 100644 --- a/rxjava-core/build.gradle +++ b/rxjava-core/build.gradle @@ -1,6 +1,10 @@ apply plugin: 'java' apply plugin: 'eclipse' +// we want to target Java 1.5 so this can be used on Android +sourceCompatibility = JavaVersion.VERSION_1_5 +targetCompatibility = JavaVersion.VERSION_1_5 + dependencies { compile 'org.slf4j:slf4j-api:1.7.0' compile 'com.google.code.findbugs:jsr305:2.0.0' diff --git a/rxjava-core/src/main/java/org/rx/functions/Func0.java b/rxjava-core/src/main/java/org/rx/functions/Func0.java index b35de70a45..224ccb1800 100644 --- a/rxjava-core/src/main/java/org/rx/functions/Func0.java +++ b/rxjava-core/src/main/java/org/rx/functions/Func0.java @@ -2,4 +2,4 @@ public interface Func0 { public R call(); -} \ No newline at end of file +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/functions/Func1.java b/rxjava-core/src/main/java/org/rx/functions/Func1.java index 4be213e5fc..8bb6b158d1 100644 --- a/rxjava-core/src/main/java/org/rx/functions/Func1.java +++ b/rxjava-core/src/main/java/org/rx/functions/Func1.java @@ -2,4 +2,4 @@ public interface Func1 { public R call(T1 t1); -} \ No newline at end of file +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/functions/Func2.java b/rxjava-core/src/main/java/org/rx/functions/Func2.java index 35b1e9254a..afb3649064 100644 --- a/rxjava-core/src/main/java/org/rx/functions/Func2.java +++ b/rxjava-core/src/main/java/org/rx/functions/Func2.java @@ -2,4 +2,4 @@ public interface Func2 { public R call(T1 t1, T2 t2); -} \ No newline at end of file +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/functions/Func3.java b/rxjava-core/src/main/java/org/rx/functions/Func3.java index 427badd67f..a38a374eb7 100644 --- a/rxjava-core/src/main/java/org/rx/functions/Func3.java +++ b/rxjava-core/src/main/java/org/rx/functions/Func3.java @@ -2,4 +2,4 @@ public interface Func3 { public R call(T1 t1, T2 t2, T3 t3); -} \ No newline at end of file +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/functions/Func4.java b/rxjava-core/src/main/java/org/rx/functions/Func4.java index 95f917365d..6ab1b11d1c 100644 --- a/rxjava-core/src/main/java/org/rx/functions/Func4.java +++ b/rxjava-core/src/main/java/org/rx/functions/Func4.java @@ -1,5 +1,5 @@ package org.rx.functions; public interface Func4 { - public R call(T1 t1, T2 t2, T3 t3, T4 t4); + public R call(T1 t1, T2 t2, T3 t3, T4 t4); } \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/AtomicWatchableSubscription.java b/rxjava-core/src/main/java/org/rx/operations/AtomicObservableSubscription.java similarity index 67% rename from rxjava-core/src/main/java/org/rx/operations/AtomicWatchableSubscription.java rename to rxjava-core/src/main/java/org/rx/operations/AtomicObservableSubscription.java index 8a58f5091d..055d0025dd 100644 --- a/rxjava-core/src/main/java/org/rx/operations/AtomicWatchableSubscription.java +++ b/rxjava-core/src/main/java/org/rx/operations/AtomicObservableSubscription.java @@ -5,23 +5,22 @@ import javax.annotation.concurrent.ThreadSafe; -import org.rx.reactive.IDisposable; - +import org.rx.reactive.Subscription; /** - * Thread-safe wrapper around WatchableSubscription that ensures unsubscribe can be called only once. + * Thread-safe wrapper around ObservableSubscription that ensures unsubscribe can be called only once. */ @ThreadSafe -/* package */class AtomicWatchableSubscription implements IDisposable { +/* package */final class AtomicObservableSubscription implements Subscription { - private AtomicReference actualSubscription = new AtomicReference(); + private AtomicReference actualSubscription = new AtomicReference(); private AtomicBoolean unsubscribed = new AtomicBoolean(false); - public AtomicWatchableSubscription() { + public AtomicObservableSubscription() { } - public AtomicWatchableSubscription(IDisposable actualSubscription) { + public AtomicObservableSubscription(Subscription actualSubscription) { this.actualSubscription.set(actualSubscription); } @@ -32,7 +31,7 @@ public AtomicWatchableSubscription(IDisposable actualSubscription) { * @throws IllegalStateException * if trying to set more than once (or use this method after setting via constructor) */ - public AtomicWatchableSubscription setActual(IDisposable actualSubscription) { + public AtomicObservableSubscription setActual(Subscription actualSubscription) { if (!this.actualSubscription.compareAndSet(null, actualSubscription)) { throw new IllegalStateException("Can not set subscription more than once."); } @@ -42,7 +41,7 @@ public AtomicWatchableSubscription setActual(IDisposable actualSubscription) { @Override public void unsubscribe() { // get the real thing and set to null in an atomic operation so we will only ever call unsubscribe once - IDisposable actual = actualSubscription.getAndSet(null); + Subscription actual = actualSubscription.getAndSet(null); // if it's not null we will unsubscribe if (actual != null) { actual.unsubscribe(); diff --git a/rxjava-core/src/main/java/org/rx/operations/AtomicWatcher.java b/rxjava-core/src/main/java/org/rx/operations/AtomicObserver.java similarity index 73% rename from rxjava-core/src/main/java/org/rx/operations/AtomicWatcher.java rename to rxjava-core/src/main/java/org/rx/operations/AtomicObserver.java index 6a9cb4dc60..aafe260ae0 100644 --- a/rxjava-core/src/main/java/org/rx/operations/AtomicWatcher.java +++ b/rxjava-core/src/main/java/org/rx/operations/AtomicObserver.java @@ -2,13 +2,13 @@ import javax.annotation.concurrent.ThreadSafe; -import org.rx.reactive.IObserver; +import org.rx.reactive.Observer; /** - * A thread-safe Watcher for transitioning states in operators. + * A thread-safe Observer for transitioning states in operators. *

* Allows both single-threaded and multi-threaded execution controlled by the following FastProperty: - *

  • reactive.watcher.multithreaded.enabled [Default: false]
  • + *
  • reactive.Observer.multithreaded.enabled [Default: false]
  • *

    * Single-threaded Execution rules are: *

      @@ -29,7 +29,7 @@ * @param */ @ThreadSafe -/* package */class AtomicWatcher implements IObserver { +/* package */final class AtomicObserver implements Observer { /** Allow changing between forcing single or allowing multi-threaded execution of onNext */ private static boolean allowMultiThreaded = true; @@ -41,29 +41,29 @@ } } - private final IObserver watcher; + private final Observer Observer; - public AtomicWatcher(IObserver watcher, AtomicWatchableSubscription subscription) { + public AtomicObserver(Observer Observer, AtomicObservableSubscription subscription) { if (allowMultiThreaded) { - this.watcher = new AtomicWatcherMultiThreaded(watcher, subscription); + this.Observer = new AtomicObserverMultiThreaded(Observer, subscription); } else { - this.watcher = new AtomicWatcherSingleThreaded(watcher, subscription); + this.Observer = new AtomicObserverSingleThreaded(Observer, subscription); } } @Override public void onCompleted() { - watcher.onCompleted(); + Observer.onCompleted(); } @Override public void onError(Exception e) { - watcher.onError(e); + Observer.onError(e); } @Override public void onNext(T args) { - watcher.onNext(args); + Observer.onNext(args); } } diff --git a/rxjava-core/src/main/java/org/rx/operations/AtomicWatcherMultiThreaded.java b/rxjava-core/src/main/java/org/rx/operations/AtomicObserverMultiThreaded.java similarity index 79% rename from rxjava-core/src/main/java/org/rx/operations/AtomicWatcherMultiThreaded.java rename to rxjava-core/src/main/java/org/rx/operations/AtomicObserverMultiThreaded.java index c4d2ab75da..e267950b51 100644 --- a/rxjava-core/src/main/java/org/rx/operations/AtomicWatcherMultiThreaded.java +++ b/rxjava-core/src/main/java/org/rx/operations/AtomicObserverMultiThreaded.java @@ -18,13 +18,12 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; /** - * A thread-safe Watcher for transitioning states in operators. + * A thread-safe Observer for transitioning states in operators. *

      * Execution rules are: *

        @@ -38,15 +37,15 @@ * @param */ @ThreadSafe -/* package */class AtomicWatcherMultiThreaded implements IObserver { +/* package */final class AtomicObserverMultiThreaded implements Observer { - private final IObserver watcher; - private final AtomicWatchableSubscription subscription; + private final Observer Observer; + private final AtomicObservableSubscription subscription; private final Sync sync = new Sync(); private volatile boolean finishRequested = false; - public AtomicWatcherMultiThreaded(IObserver watcher, AtomicWatchableSubscription subscription) { - this.watcher = watcher; + public AtomicObserverMultiThreaded(Observer Observer, AtomicObservableSubscription subscription) { + this.Observer = Observer; this.subscription = subscription; } @@ -69,7 +68,7 @@ public void onNext(T arg) { } // immediately enter a try/finally that will release the lock once done the work try { - watcher.onNext(arg); + Observer.onNext(arg); } finally { // we finished this work so release it sync.releaseShared(Sync.TYPE_NEXT); @@ -107,7 +106,7 @@ public void onError(Exception e) { } // immediately enter a try/finally that will release the lock once done the work try { - watcher.onError(e); + Observer.onError(e); } finally { // we finished this work so release it sync.release(Sync.TYPE_FINISH); @@ -145,7 +144,7 @@ public void onCompleted() { } // immediately enter a try/finally that will release the lock once done the work try { - watcher.onCompleted(); + Observer.onCompleted(); } finally { // we finished this work so release it sync.release(Sync.TYPE_FINISH); @@ -306,7 +305,7 @@ public boolean isNotTerminalState(int state) { public static class UnitTest { @Mock - IObserver aWatcher; + Observer aObserver; @Before public void before() { @@ -315,53 +314,53 @@ public void before() { @Test public void testSingleThreadedBasic() { - IDisposable s = mock(IDisposable.class); - TestSingleThreadedWatchable w = new TestSingleThreadedWatchable(s, "one", "two", "three"); + Subscription s = mock(Subscription.class); + TestSingleThreadedObservable w = new TestSingleThreadedObservable(s, "one", "two", "three"); - AtomicWatchableSubscription as = new AtomicWatchableSubscription(s); - AtomicWatcherMultiThreaded aw = new AtomicWatcherMultiThreaded(aWatcher, as); + AtomicObservableSubscription as = new AtomicObservableSubscription(s); + AtomicObserverMultiThreaded aw = new AtomicObserverMultiThreaded(aObserver, as); w.subscribe(aw); w.waitToFinish(); - verify(aWatcher, times(1)).onNext("one"); - verify(aWatcher, times(1)).onNext("two"); - verify(aWatcher, times(1)).onNext("three"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); verify(s, never()).unsubscribe(); } @Test public void testMultiThreadedBasic() { - IDisposable s = mock(IDisposable.class); - TestMultiThreadedWatchable w = new TestMultiThreadedWatchable(s, "one", "two", "three"); + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable w = new TestMultiThreadedObservable(s, "one", "two", "three"); - AtomicWatchableSubscription as = new AtomicWatchableSubscription(s); - BusyWatcher busyWatcher = new BusyWatcher(); - AtomicWatcherMultiThreaded aw = new AtomicWatcherMultiThreaded(busyWatcher, as); + AtomicObservableSubscription as = new AtomicObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + AtomicObserverMultiThreaded aw = new AtomicObserverMultiThreaded(busyObserver, as); w.subscribe(aw); w.waitToFinish(); - assertEquals(3, busyWatcher.onNextCount.get()); - assertFalse(busyWatcher.onError); - assertTrue(busyWatcher.onCompleted); + assertEquals(3, busyObserver.onNextCount.get()); + assertFalse(busyObserver.onError); + assertTrue(busyObserver.onCompleted); verify(s, never()).unsubscribe(); assertTrue(w.maxConcurrentThreads.get() > 1); - assertTrue(busyWatcher.maxConcurrentThreads.get() > 1); + assertTrue(busyObserver.maxConcurrentThreads.get() > 1); System.out.println("maxConcurrentThreads: " + w.maxConcurrentThreads.get()); } @Test public void testMultiThreadedWithNPE() { - IDisposable s = mock(IDisposable.class); - TestMultiThreadedWatchable w = new TestMultiThreadedWatchable(s, "one", "two", "three", null); + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable w = new TestMultiThreadedObservable(s, "one", "two", "three", null); - AtomicWatchableSubscription as = new AtomicWatchableSubscription(s); - BusyWatcher busyWatcher = new BusyWatcher(); - AtomicWatcherMultiThreaded aw = new AtomicWatcherMultiThreaded(busyWatcher, as); + AtomicObservableSubscription as = new AtomicObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + AtomicObserverMultiThreaded aw = new AtomicObserverMultiThreaded(busyObserver, as); w.subscribe(aw); w.waitToFinish(); @@ -373,40 +372,40 @@ public void testMultiThreadedWithNPE() { * so the null could cause onError to occur before one or more of the other values * resulting in less onNext calls than 3. */ - assertTrue(busyWatcher.onNextCount.get() >= 0 && busyWatcher.onNextCount.get() <= 3); + assertTrue(busyObserver.onNextCount.get() >= 0 && busyObserver.onNextCount.get() <= 3); // we expect an onError because of the null throwing an NPE - assertTrue(busyWatcher.onError); + assertTrue(busyObserver.onError); // no onCompleted because onError was invoked - assertFalse(busyWatcher.onCompleted); + assertFalse(busyObserver.onCompleted); verify(s, never()).unsubscribe(); assertTrue(w.maxConcurrentThreads.get() > 1); - assertTrue(busyWatcher.maxConcurrentThreads.get() > 1); + assertTrue(busyObserver.maxConcurrentThreads.get() > 1); } @Test public void testMultiThreadedWithNPEinMiddle() { - IDisposable s = mock(IDisposable.class); - TestMultiThreadedWatchable w = new TestMultiThreadedWatchable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable w = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); - AtomicWatchableSubscription as = new AtomicWatchableSubscription(s); - BusyWatcher busyWatcher = new BusyWatcher(); - AtomicWatcherMultiThreaded aw = new AtomicWatcherMultiThreaded(busyWatcher, as); + AtomicObservableSubscription as = new AtomicObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + AtomicObserverMultiThreaded aw = new AtomicObserverMultiThreaded(busyObserver, as); w.subscribe(aw); w.waitToFinish(); System.out.println("maxConcurrentThreads: " + w.maxConcurrentThreads.get()); // this should not be the full number of items since the error should stop it before it completes all 9 - System.out.println("onNext count: " + busyWatcher.onNextCount.get()); - assertTrue(busyWatcher.onNextCount.get() < 9); - assertTrue(busyWatcher.onError); + System.out.println("onNext count: " + busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 9); + assertTrue(busyObserver.onError); // no onCompleted because onError was invoked - assertFalse(busyWatcher.onCompleted); + assertFalse(busyObserver.onCompleted); verify(s, never()).unsubscribe(); assertTrue(w.maxConcurrentThreads.get() > 1); - assertTrue(busyWatcher.maxConcurrentThreads.get() > 1); + assertTrue(busyObserver.maxConcurrentThreads.get() > 1); } /** @@ -420,9 +419,9 @@ public void testMultiThreadedWithNPEinMiddle() { public void runConcurrencyTest() { ExecutorService tp = Executors.newFixedThreadPool(20); try { - TestConcurrencyWatcher tw = new TestConcurrencyWatcher(); - AtomicWatchableSubscription s = new AtomicWatchableSubscription(); - AtomicWatcherMultiThreaded w = new AtomicWatcherMultiThreaded(tw, s); + TestConcurrencyObserver tw = new TestConcurrencyObserver(); + AtomicObservableSubscription s = new AtomicObservableSubscription(); + AtomicObserverMultiThreaded w = new AtomicObserverMultiThreaded(tw, s); Future f1 = tp.submit(new OnNextThread(w, 12000)); Future f2 = tp.submit(new OnNextThread(w, 5000)); @@ -431,7 +430,7 @@ public void runConcurrencyTest() { Future f5 = tp.submit(new OnNextThread(w, 22000)); Future f6 = tp.submit(new OnNextThread(w, 15000)); - Future f10 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onCompleted, f1, f2, f3, f4)); + Future f10 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f1, f2, f3, f4)); try { Thread.sleep(10); } catch (InterruptedException e) { @@ -442,15 +441,15 @@ public void runConcurrencyTest() { Future f7 = tp.submit(new OnNextThread(w, 7500)); Future f8 = tp.submit(new OnNextThread(w, 23500)); - Future f11 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onCompleted, f4, f6, f7)); - Future f12 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onCompleted, f4, f6, f7)); - Future f13 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onCompleted, f4, f6, f7)); - Future f14 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onCompleted, f4, f6, f7)); + Future f11 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f12 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f13 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f14 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); // // the next 4 onError events should wait on same as f10 - Future f15 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onError, f1, f2, f3, f4)); - Future f16 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onError, f1, f2, f3, f4)); - Future f17 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onError, f1, f2, f3, f4)); - Future f18 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onError, f1, f2, f3, f4)); + Future f15 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f16 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f17 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f18 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); waitOnThreads(f1, f2, f3, f4, f5, f6, f7, f8, f10, f11, f12, f13, f14, f15, f16, f17, f18); int numNextEvents = tw.assertEvents(); @@ -472,28 +471,28 @@ public void runConcurrencyTest() { * This spawns a single thread for the subscribe execution * */ - private static class TestSingleThreadedWatchable extends AbstractIObservable { + private static class TestSingleThreadedObservable extends Observable { - final IDisposable s; + final Subscription s; final String[] values; Thread t = null; - public TestSingleThreadedWatchable(IDisposable s, String... values) { + public TestSingleThreadedObservable(Subscription s, String... values) { this.s = s; this.values = values; } @Override - public IDisposable subscribe(final IObserver observer) { - System.out.println("TestSingleThreadedWatchable subscribed to ..."); + public Subscription subscribe(final Observer observer) { + System.out.println("TestSingleThreadedObservable subscribed to ..."); t = new Thread(new Runnable() { @Override public void run() { try { - System.out.println("running TestSingleThreadedWatchable thread"); + System.out.println("running TestSingleThreadedObservable thread"); for (String s : values) { - System.out.println("TestSingleThreadedWatchable onNext: " + s); + System.out.println("TestSingleThreadedObservable onNext: " + s); observer.onNext(s); } observer.onCompleted(); @@ -503,9 +502,9 @@ public void run() { } }); - System.out.println("starting TestSingleThreadedWatchable thread"); + System.out.println("starting TestSingleThreadedObservable thread"); t.start(); - System.out.println("done starting TestSingleThreadedWatchable thread"); + System.out.println("done starting TestSingleThreadedObservable thread"); return s; } @@ -523,30 +522,30 @@ public void waitToFinish() { * This spawns a thread for the subscription, then a separate thread for each onNext call. * */ - private static class TestMultiThreadedWatchable extends AbstractIObservable { + private static class TestMultiThreadedObservable extends Observable { - final IDisposable s; + final Subscription s; final String[] values; Thread t = null; AtomicInteger threadsRunning = new AtomicInteger(); AtomicInteger maxConcurrentThreads = new AtomicInteger(); ExecutorService threadPool; - public TestMultiThreadedWatchable(IDisposable s, String... values) { + public TestMultiThreadedObservable(Subscription s, String... values) { this.s = s; this.values = values; this.threadPool = Executors.newCachedThreadPool(); } @Override - public IDisposable subscribe(final IObserver observer) { - System.out.println("TestMultiThreadedWatchable subscribed to ..."); + public Subscription subscribe(final Observer observer) { + System.out.println("TestMultiThreadedObservable subscribed to ..."); t = new Thread(new Runnable() { @Override public void run() { try { - System.out.println("running TestMultiThreadedWatchable thread"); + System.out.println("running TestMultiThreadedObservable thread"); for (final String s : values) { threadPool.execute(new Runnable() { @@ -555,7 +554,7 @@ public void run() { threadsRunning.incrementAndGet(); try { // perform onNext call - System.out.println("TestMultiThreadedWatchable onNext: " + s); + System.out.println("TestMultiThreadedObservable onNext: " + s); if (s == null) { // force an error throw new NullPointerException(); @@ -591,9 +590,9 @@ public void run() { observer.onCompleted(); } }); - System.out.println("starting TestMultiThreadedWatchable thread"); + System.out.println("starting TestMultiThreadedObservable thread"); t.start(); - System.out.println("done starting TestMultiThreadedWatchable thread"); + System.out.println("done starting TestMultiThreadedObservable thread"); return s; } @@ -606,7 +605,7 @@ public void waitToFinish() { } } - private static class BusyWatcher implements IObserver { + private static class BusyObserver implements Observer { volatile boolean onCompleted = false; volatile boolean onError = false; AtomicInteger onNextCount = new AtomicInteger(); @@ -615,13 +614,13 @@ private static class BusyWatcher implements IObserver { @Override public void onCompleted() { - System.out.println(">>> BusyWatcher received onCompleted"); + System.out.println(">>> BusyObserver received onCompleted"); onCompleted = true; } @Override public void onError(Exception e) { - System.out.println(">>> BusyWatcher received onError: " + e.getMessage()); + System.out.println(">>> BusyObserver received onError: " + e.getMessage()); onError = true; } @@ -630,7 +629,7 @@ public void onNext(String args) { threadsRunning.incrementAndGet(); try { onNextCount.incrementAndGet(); - System.out.println(">>> BusyWatcher received onNext: " + args); + System.out.println(">>> BusyObserver received onNext: " + args); try { // simulate doing something computational Thread.sleep(200); @@ -650,37 +649,38 @@ public void onNext(String args) { } - private static enum TestConcurrencyWatcherEvent { + private static enum TestConcurrencyObserverEvent { onCompleted, onError, onNext; } - private static class TestConcurrencyWatcher implements IObserver { + private static class TestConcurrencyObserver implements Observer { /** used to store the order and number of events received */ - private final LinkedBlockingQueue events = new LinkedBlockingQueue(); + private final LinkedBlockingQueue events = new LinkedBlockingQueue(); private final int waitTime; - public TestConcurrencyWatcher(int waitTimeInNext) { + @SuppressWarnings("unused") + public TestConcurrencyObserver(int waitTimeInNext) { this.waitTime = waitTimeInNext; } - public TestConcurrencyWatcher() { + public TestConcurrencyObserver() { this.waitTime = 0; } @Override public void onCompleted() { - events.add(TestConcurrencyWatcherEvent.onCompleted); + events.add(TestConcurrencyObserverEvent.onCompleted); } @Override public void onError(Exception e) { - events.add(TestConcurrencyWatcherEvent.onError); + events.add(TestConcurrencyObserverEvent.onError); } @Override public void onNext(String args) { - events.add(TestConcurrencyWatcherEvent.onNext); + events.add(TestConcurrencyObserverEvent.onNext); // do some artificial work to make the thread scheduling/timing vary int s = 0; for (int i = 0; i < 20; i++) { @@ -707,20 +707,20 @@ public void onNext(String args) { public int assertEvents() throws IllegalStateException { int nextCount = 0; boolean finished = false; - for (TestConcurrencyWatcherEvent e : events) { - if (e == TestConcurrencyWatcherEvent.onNext) { + for (TestConcurrencyObserverEvent e : events) { + if (e == TestConcurrencyObserverEvent.onNext) { if (finished) { // already finished, we shouldn't get this again throw new IllegalStateException("Received onNext but we're already finished."); } nextCount++; - } else if (e == TestConcurrencyWatcherEvent.onError) { + } else if (e == TestConcurrencyObserverEvent.onError) { if (finished) { // already finished, we shouldn't get this again throw new IllegalStateException("Received onError but we're already finished."); } finished = true; - } else if (e == TestConcurrencyWatcherEvent.onCompleted) { + } else if (e == TestConcurrencyObserverEvent.onCompleted) { if (finished) { // already finished, we shouldn't get this again throw new IllegalStateException("Received onCompleted but we're already finished."); @@ -739,18 +739,18 @@ public int assertEvents() throws IllegalStateException { */ public static class OnNextThread implements Runnable { - private final IObserver watcher; + private final Observer Observer; private final int numStringsToSend; - OnNextThread(IObserver watcher, int numStringsToSend) { - this.watcher = watcher; + OnNextThread(Observer Observer, int numStringsToSend) { + this.Observer = Observer; this.numStringsToSend = numStringsToSend; } @Override public void run() { for (int i = 0; i < numStringsToSend; i++) { - watcher.onNext("aString"); + Observer.onNext("aString"); } } } @@ -760,12 +760,12 @@ public void run() { */ public static class CompletionThread implements Runnable { - private final IObserver watcher; - private final TestConcurrencyWatcherEvent event; + private final Observer Observer; + private final TestConcurrencyObserverEvent event; private final Future[] waitOnThese; - CompletionThread(IObserver watcher, TestConcurrencyWatcherEvent event, Future... waitOnThese) { - this.watcher = watcher; + CompletionThread(Observer Observer, TestConcurrencyObserverEvent event, Future... waitOnThese) { + this.Observer = Observer; this.event = event; this.waitOnThese = waitOnThese; } @@ -784,10 +784,10 @@ public void run() { } /* send the event */ - if (event == TestConcurrencyWatcherEvent.onError) { - watcher.onError(new RuntimeException("mocked exception")); - } else if (event == TestConcurrencyWatcherEvent.onCompleted) { - watcher.onCompleted(); + if (event == TestConcurrencyObserverEvent.onError) { + Observer.onError(new RuntimeException("mocked exception")); + } else if (event == TestConcurrencyObserverEvent.onCompleted) { + Observer.onCompleted(); } else { throw new IllegalArgumentException("Expecting either onError or onCompleted"); diff --git a/rxjava-core/src/main/java/org/rx/operations/AtomicWatcherSingleThreaded.java b/rxjava-core/src/main/java/org/rx/operations/AtomicObserverSingleThreaded.java similarity index 70% rename from rxjava-core/src/main/java/org/rx/operations/AtomicWatcherSingleThreaded.java rename to rxjava-core/src/main/java/org/rx/operations/AtomicObserverSingleThreaded.java index 1a727f714f..74bed86347 100644 --- a/rxjava-core/src/main/java/org/rx/operations/AtomicWatcherSingleThreaded.java +++ b/rxjava-core/src/main/java/org/rx/operations/AtomicObserverSingleThreaded.java @@ -17,13 +17,12 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; /** - * A thread-safe Watcher for transitioning states in operators. + * A thread-safe Observer for transitioning states in operators. *

        * Execution rules are: *

          @@ -35,27 +34,27 @@ * @param */ @ThreadSafe -/* package */class AtomicWatcherSingleThreaded implements IObserver { +/* package */final class AtomicObserverSingleThreaded implements Observer { /** * Intrinsic synchronized locking with double-check short-circuiting was chosen after testing several other implementations. * * The code and results can be found here: - * - https://github.com/benjchristensen/JavaLockPerformanceTests/tree/master/results/watcher - * - https://github.com/benjchristensen/JavaLockPerformanceTests/tree/master/src/com/benjchristensen/performance/locks/watcher + * - https://github.com/benjchristensen/JavaLockPerformanceTests/tree/master/results/Observer + * - https://github.com/benjchristensen/JavaLockPerformanceTests/tree/master/src/com/benjchristensen/performance/locks/Observer * * The major characteristic that made me choose synchronized instead of Reentrant or a customer AbstractQueueSynchronizer implementation - * is that intrinsic locking performed better when nested, and AtomicWatcherSingleThreaded will end up nested most of the time since Rx is + * is that intrinsic locking performed better when nested, and AtomicObserverSingleThreaded will end up nested most of the time since Rx is * compositional by its very nature. */ - private final IObserver watcher; - private final AtomicWatchableSubscription subscription; + private final Observer Observer; + private final AtomicObservableSubscription subscription; private volatile boolean finishRequested = false; private volatile boolean finished = false; - public AtomicWatcherSingleThreaded(IObserver watcher, AtomicWatchableSubscription subscription) { - this.watcher = watcher; + public AtomicObserverSingleThreaded(Observer Observer, AtomicObservableSubscription subscription) { + this.Observer = Observer; this.subscription = subscription; } @@ -70,7 +69,7 @@ public void onNext(T arg) { // if we're already stopped, or a finish request has been received, we won't allow further onNext requests return; } - watcher.onNext(arg); + Observer.onNext(arg); } } @@ -85,7 +84,7 @@ public void onError(Exception e) { if (finished || subscription.isUnsubscribed()) { return; } - watcher.onError(e); + Observer.onError(e); finished = true; } } @@ -101,14 +100,14 @@ public void onCompleted() { if (finished || subscription.isUnsubscribed()) { return; } - watcher.onCompleted(); + Observer.onCompleted(); finished = true; } } public static class UnitTest { @Mock - IObserver aWatcher; + Observer aObserver; @Before public void before() { @@ -117,54 +116,54 @@ public void before() { @Test public void testSingleThreadedBasic() { - IDisposable s = mock(IDisposable.class); - TestSingleThreadedWatchable w = new TestSingleThreadedWatchable(s, "one", "two", "three"); + Subscription s = mock(Subscription.class); + TestSingleThreadedObservable w = new TestSingleThreadedObservable(s, "one", "two", "three"); - AtomicWatchableSubscription as = new AtomicWatchableSubscription(s); - AtomicWatcherSingleThreaded aw = new AtomicWatcherSingleThreaded(aWatcher, as); + AtomicObservableSubscription as = new AtomicObservableSubscription(s); + AtomicObserverSingleThreaded aw = new AtomicObserverSingleThreaded(aObserver, as); w.subscribe(aw); w.waitToFinish(); - verify(aWatcher, times(1)).onNext("one"); - verify(aWatcher, times(1)).onNext("two"); - verify(aWatcher, times(1)).onNext("three"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); verify(s, never()).unsubscribe(); } @Test public void testMultiThreadedBasic() { - IDisposable s = mock(IDisposable.class); - TestMultiThreadedWatchable w = new TestMultiThreadedWatchable(s, "one", "two", "three"); + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable w = new TestMultiThreadedObservable(s, "one", "two", "three"); - AtomicWatchableSubscription as = new AtomicWatchableSubscription(s); - BusyWatcher busyWatcher = new BusyWatcher(); - AtomicWatcherSingleThreaded aw = new AtomicWatcherSingleThreaded(busyWatcher, as); + AtomicObservableSubscription as = new AtomicObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + AtomicObserverSingleThreaded aw = new AtomicObserverSingleThreaded(busyObserver, as); w.subscribe(aw); w.waitToFinish(); - assertEquals(3, busyWatcher.onNextCount.get()); - assertFalse(busyWatcher.onError); - assertTrue(busyWatcher.onCompleted); + assertEquals(3, busyObserver.onNextCount.get()); + assertFalse(busyObserver.onError); + assertTrue(busyObserver.onCompleted); verify(s, never()).unsubscribe(); // we can have concurrency ... assertTrue(w.maxConcurrentThreads.get() > 1); // ... but the onNext execution should be single threaded - assertEquals(1, busyWatcher.maxConcurrentThreads.get()); + assertEquals(1, busyObserver.maxConcurrentThreads.get()); } @Test public void testMultiThreadedWithNPE() { - IDisposable s = mock(IDisposable.class); - TestMultiThreadedWatchable w = new TestMultiThreadedWatchable(s, "one", "two", "three", null); + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable w = new TestMultiThreadedObservable(s, "one", "two", "three", null); - AtomicWatchableSubscription as = new AtomicWatchableSubscription(s); - BusyWatcher busyWatcher = new BusyWatcher(); - AtomicWatcherSingleThreaded aw = new AtomicWatcherSingleThreaded(busyWatcher, as); + AtomicObservableSubscription as = new AtomicObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + AtomicObserverSingleThreaded aw = new AtomicObserverSingleThreaded(busyObserver, as); w.subscribe(aw); w.waitToFinish(); @@ -173,44 +172,44 @@ public void testMultiThreadedWithNPE() { // we can't know how many onNext calls will occur since they each run on a separate thread // that depends on thread scheduling so 0, 1, 2 and 3 are all valid options - // assertEquals(3, busyWatcher.onNextCount.get()); - assertTrue(busyWatcher.onNextCount.get() < 4); - assertTrue(busyWatcher.onError); + // assertEquals(3, busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 4); + assertTrue(busyObserver.onError); // no onCompleted because onError was invoked - assertFalse(busyWatcher.onCompleted); + assertFalse(busyObserver.onCompleted); verify(s, never()).unsubscribe(); // we can have concurrency ... assertTrue(w.maxConcurrentThreads.get() > 1); // ... but the onNext execution should be single threaded - assertEquals(1, busyWatcher.maxConcurrentThreads.get()); + assertEquals(1, busyObserver.maxConcurrentThreads.get()); } @Test public void testMultiThreadedWithNPEinMiddle() { - IDisposable s = mock(IDisposable.class); - TestMultiThreadedWatchable w = new TestMultiThreadedWatchable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); + Subscription s = mock(Subscription.class); + TestMultiThreadedObservable w = new TestMultiThreadedObservable(s, "one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); - AtomicWatchableSubscription as = new AtomicWatchableSubscription(s); - BusyWatcher busyWatcher = new BusyWatcher(); - AtomicWatcherSingleThreaded aw = new AtomicWatcherSingleThreaded(busyWatcher, as); + AtomicObservableSubscription as = new AtomicObservableSubscription(s); + BusyObserver busyObserver = new BusyObserver(); + AtomicObserverSingleThreaded aw = new AtomicObserverSingleThreaded(busyObserver, as); w.subscribe(aw); w.waitToFinish(); System.out.println("maxConcurrentThreads: " + w.maxConcurrentThreads.get()); // this should not be the full number of items since the error should stop it before it completes all 9 - System.out.println("onNext count: " + busyWatcher.onNextCount.get()); - assertTrue(busyWatcher.onNextCount.get() < 9); - assertTrue(busyWatcher.onError); + System.out.println("onNext count: " + busyObserver.onNextCount.get()); + assertTrue(busyObserver.onNextCount.get() < 9); + assertTrue(busyObserver.onError); // no onCompleted because onError was invoked - assertFalse(busyWatcher.onCompleted); + assertFalse(busyObserver.onCompleted); verify(s, never()).unsubscribe(); // we can have concurrency ... assertTrue(w.maxConcurrentThreads.get() > 1); // ... but the onNext execution should be single threaded - assertEquals(1, busyWatcher.maxConcurrentThreads.get()); + assertEquals(1, busyObserver.maxConcurrentThreads.get()); } /** @@ -224,9 +223,9 @@ public void testMultiThreadedWithNPEinMiddle() { public void runConcurrencyTest() { ExecutorService tp = Executors.newFixedThreadPool(20); try { - TestConcurrencyWatcher tw = new TestConcurrencyWatcher(); - AtomicWatchableSubscription s = new AtomicWatchableSubscription(); - AtomicWatcherSingleThreaded w = new AtomicWatcherSingleThreaded(tw, s); + TestConcurrencyObserver tw = new TestConcurrencyObserver(); + AtomicObservableSubscription s = new AtomicObservableSubscription(); + AtomicObserverSingleThreaded w = new AtomicObserverSingleThreaded(tw, s); Future f1 = tp.submit(new OnNextThread(w, 12000)); Future f2 = tp.submit(new OnNextThread(w, 5000)); @@ -237,23 +236,24 @@ public void runConcurrencyTest() { Future f7 = tp.submit(new OnNextThread(w, 7500)); Future f8 = tp.submit(new OnNextThread(w, 23500)); - Future f10 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onCompleted, f1, f2, f3, f4)); + Future f10 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f1, f2, f3, f4)); try { Thread.sleep(1); } catch (InterruptedException e) { // ignore } - Future f11 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onCompleted, f4, f6, f7)); - Future f12 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onCompleted, f4, f6, f7)); - Future f13 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onCompleted, f4, f6, f7)); - Future f14 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onCompleted, f4, f6, f7)); + Future f11 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f12 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f13 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); + Future f14 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onCompleted, f4, f6, f7)); // // the next 4 onError events should wait on same as f10 - Future f15 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onError, f1, f2, f3, f4)); - Future f16 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onError, f1, f2, f3, f4)); - Future f17 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onError, f1, f2, f3, f4)); - Future f18 = tp.submit(new CompletionThread(w, TestConcurrencyWatcherEvent.onError, f1, f2, f3, f4)); + Future f15 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f16 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f17 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); + Future f18 = tp.submit(new CompletionThread(w, TestConcurrencyObserverEvent.onError, f1, f2, f3, f4)); waitOnThreads(f1, f2, f3, f4, f5, f6, f7, f8, f10, f11, f12, f13, f14, f15, f16, f17, f18); + @SuppressWarnings("unused") int numNextEvents = tw.assertEvents(null); // no check of type since we don't want to test barging results here, just interleaving behavior // System.out.println("Number of events executed: " + numNextEvents); } catch (Exception e) { @@ -285,18 +285,18 @@ private static void waitOnThreads(Future... futures) { */ public static class OnNextThread implements Runnable { - private final IObserver watcher; + private final Observer Observer; private final int numStringsToSend; - OnNextThread(IObserver watcher, int numStringsToSend) { - this.watcher = watcher; + OnNextThread(Observer Observer, int numStringsToSend) { + this.Observer = Observer; this.numStringsToSend = numStringsToSend; } @Override public void run() { for (int i = 0; i < numStringsToSend; i++) { - watcher.onNext("aString"); + Observer.onNext("aString"); } } } @@ -306,12 +306,12 @@ public void run() { */ public static class CompletionThread implements Runnable { - private final IObserver watcher; - private final TestConcurrencyWatcherEvent event; + private final Observer Observer; + private final TestConcurrencyObserverEvent event; private final Future[] waitOnThese; - CompletionThread(IObserver watcher, TestConcurrencyWatcherEvent event, Future... waitOnThese) { - this.watcher = watcher; + CompletionThread(Observer Observer, TestConcurrencyObserverEvent event, Future... waitOnThese) { + this.Observer = Observer; this.event = event; this.waitOnThese = waitOnThese; } @@ -330,10 +330,10 @@ public void run() { } /* send the event */ - if (event == TestConcurrencyWatcherEvent.onError) { - watcher.onError(new RuntimeException("mocked exception")); - } else if (event == TestConcurrencyWatcherEvent.onCompleted) { - watcher.onCompleted(); + if (event == TestConcurrencyObserverEvent.onError) { + Observer.onError(new RuntimeException("mocked exception")); + } else if (event == TestConcurrencyObserverEvent.onCompleted) { + Observer.onCompleted(); } else { throw new IllegalArgumentException("Expecting either onError or onCompleted"); @@ -341,37 +341,38 @@ public void run() { } } - private static enum TestConcurrencyWatcherEvent { + private static enum TestConcurrencyObserverEvent { onCompleted, onError, onNext; } - private static class TestConcurrencyWatcher implements IObserver { + private static class TestConcurrencyObserver implements Observer { /** used to store the order and number of events received */ - private final LinkedBlockingQueue events = new LinkedBlockingQueue(); + private final LinkedBlockingQueue events = new LinkedBlockingQueue(); private final int waitTime; - public TestConcurrencyWatcher(int waitTimeInNext) { + @SuppressWarnings("unused") + public TestConcurrencyObserver(int waitTimeInNext) { this.waitTime = waitTimeInNext; } - public TestConcurrencyWatcher() { + public TestConcurrencyObserver() { this.waitTime = 0; } @Override public void onCompleted() { - events.add(TestConcurrencyWatcherEvent.onCompleted); + events.add(TestConcurrencyObserverEvent.onCompleted); } @Override public void onError(Exception e) { - events.add(TestConcurrencyWatcherEvent.onError); + events.add(TestConcurrencyObserverEvent.onError); } @Override public void onNext(String args) { - events.add(TestConcurrencyWatcherEvent.onNext); + events.add(TestConcurrencyObserverEvent.onNext); // do some artificial work to make the thread scheduling/timing vary int s = 0; for (int i = 0; i < 20; i++) { @@ -395,31 +396,31 @@ public void onNext(String args) { * @throws IllegalStateException * If order of events was invalid. */ - public int assertEvents(TestConcurrencyWatcherEvent expectedEndingEvent) throws IllegalStateException { + public int assertEvents(TestConcurrencyObserverEvent expectedEndingEvent) throws IllegalStateException { int nextCount = 0; boolean finished = false; - for (TestConcurrencyWatcherEvent e : events) { - if (e == TestConcurrencyWatcherEvent.onNext) { + for (TestConcurrencyObserverEvent e : events) { + if (e == TestConcurrencyObserverEvent.onNext) { if (finished) { // already finished, we shouldn't get this again throw new IllegalStateException("Received onNext but we're already finished."); } nextCount++; - } else if (e == TestConcurrencyWatcherEvent.onError) { + } else if (e == TestConcurrencyObserverEvent.onError) { if (finished) { // already finished, we shouldn't get this again throw new IllegalStateException("Received onError but we're already finished."); } - if (expectedEndingEvent != null && TestConcurrencyWatcherEvent.onError != expectedEndingEvent) { + if (expectedEndingEvent != null && TestConcurrencyObserverEvent.onError != expectedEndingEvent) { throw new IllegalStateException("Received onError ending event but expected " + expectedEndingEvent); } finished = true; - } else if (e == TestConcurrencyWatcherEvent.onCompleted) { + } else if (e == TestConcurrencyObserverEvent.onCompleted) { if (finished) { // already finished, we shouldn't get this again throw new IllegalStateException("Received onCompleted but we're already finished."); } - if (expectedEndingEvent != null && TestConcurrencyWatcherEvent.onCompleted != expectedEndingEvent) { + if (expectedEndingEvent != null && TestConcurrencyObserverEvent.onCompleted != expectedEndingEvent) { throw new IllegalStateException("Received onCompleted ending event but expected " + expectedEndingEvent); } finished = true; @@ -435,28 +436,28 @@ public int assertEvents(TestConcurrencyWatcherEvent expectedEndingEvent) throws * This spawns a single thread for the subscribe execution * */ - private static class TestSingleThreadedWatchable extends AbstractIObservable { + private static class TestSingleThreadedObservable extends Observable { - final IDisposable s; + final Subscription s; final String[] values; Thread t = null; - public TestSingleThreadedWatchable(IDisposable s, String... values) { + public TestSingleThreadedObservable(Subscription s, String... values) { this.s = s; this.values = values; } @Override - public IDisposable subscribe(final IObserver observer) { - System.out.println("TestSingleThreadedWatchable subscribed to ..."); + public Subscription subscribe(final Observer observer) { + System.out.println("TestSingleThreadedObservable subscribed to ..."); t = new Thread(new Runnable() { @Override public void run() { try { - System.out.println("running TestSingleThreadedWatchable thread"); + System.out.println("running TestSingleThreadedObservable thread"); for (String s : values) { - System.out.println("TestSingleThreadedWatchable onNext: " + s); + System.out.println("TestSingleThreadedObservable onNext: " + s); observer.onNext(s); } observer.onCompleted(); @@ -466,9 +467,9 @@ public void run() { } }); - System.out.println("starting TestSingleThreadedWatchable thread"); + System.out.println("starting TestSingleThreadedObservable thread"); t.start(); - System.out.println("done starting TestSingleThreadedWatchable thread"); + System.out.println("done starting TestSingleThreadedObservable thread"); return s; } @@ -486,30 +487,30 @@ public void waitToFinish() { * This spawns a thread for the subscription, then a separate thread for each onNext call. * */ - private static class TestMultiThreadedWatchable extends AbstractIObservable { + private static class TestMultiThreadedObservable extends Observable { - final IDisposable s; + final Subscription s; final String[] values; Thread t = null; AtomicInteger threadsRunning = new AtomicInteger(); AtomicInteger maxConcurrentThreads = new AtomicInteger(); ExecutorService threadPool; - public TestMultiThreadedWatchable(IDisposable s, String... values) { + public TestMultiThreadedObservable(Subscription s, String... values) { this.s = s; this.values = values; this.threadPool = Executors.newCachedThreadPool(); } @Override - public IDisposable subscribe(final IObserver observer) { - System.out.println("TestMultiThreadedWatchable subscribed to ..."); + public Subscription subscribe(final Observer observer) { + System.out.println("TestMultiThreadedObservable subscribed to ..."); t = new Thread(new Runnable() { @Override public void run() { try { - System.out.println("running TestMultiThreadedWatchable thread"); + System.out.println("running TestMultiThreadedObservable thread"); for (final String s : values) { threadPool.execute(new Runnable() { @@ -518,7 +519,7 @@ public void run() { threadsRunning.incrementAndGet(); try { // perform onNext call - System.out.println("TestMultiThreadedWatchable onNext: " + s); + System.out.println("TestMultiThreadedObservable onNext: " + s); if (s == null) { // force an error throw new NullPointerException(); @@ -554,9 +555,9 @@ public void run() { observer.onCompleted(); } }); - System.out.println("starting TestMultiThreadedWatchable thread"); + System.out.println("starting TestMultiThreadedObservable thread"); t.start(); - System.out.println("done starting TestMultiThreadedWatchable thread"); + System.out.println("done starting TestMultiThreadedObservable thread"); return s; } @@ -569,7 +570,7 @@ public void waitToFinish() { } } - private static class BusyWatcher implements IObserver { + private static class BusyObserver implements Observer { volatile boolean onCompleted = false; volatile boolean onError = false; AtomicInteger onNextCount = new AtomicInteger(); @@ -578,13 +579,13 @@ private static class BusyWatcher implements IObserver { @Override public void onCompleted() { - System.out.println(">>> BusyWatcher received onCompleted"); + System.out.println(">>> BusyObserver received onCompleted"); onCompleted = true; } @Override public void onError(Exception e) { - System.out.println(">>> BusyWatcher received onError: " + e.getMessage()); + System.out.println(">>> BusyObserver received onError: " + e.getMessage()); onError = true; } @@ -593,7 +594,7 @@ public void onNext(String args) { threadsRunning.incrementAndGet(); try { onNextCount.incrementAndGet(); - System.out.println(">>> BusyWatcher received onNext: " + args); + System.out.println(">>> BusyObserver received onNext: " + args); try { // simulate doing something computational Thread.sleep(200); diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationCombineLatest.java b/rxjava-core/src/main/java/org/rx/operations/OperationCombineLatest.java index 63907d5f18..dc601e9568 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationCombineLatest.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationCombineLatest.java @@ -18,44 +18,42 @@ import org.rx.functions.Func4; import org.rx.functions.FuncN; import org.rx.functions.Functions; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; +public class OperationCombineLatest { -class OperationCombineLatest { - - public static IObservable combineLatest(IObservable w0, IObservable w1, Func2 combineLatestFunction) { + public static Observable combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); - a.addWatcher(new CombineWatcher(a, w0)); - a.addWatcher(new CombineWatcher(a, w1)); + a.addObserver(new CombineObserver(a, w0)); + a.addObserver(new CombineObserver(a, w1)); return a; } - public static IObservable combineLatest(IObservable w0, IObservable w1, IObservable w2, Func3 combineLatestFunction) { + public static Observable combineLatest(Observable w0, Observable w1, Observable w2, Func3 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); - a.addWatcher(new CombineWatcher(a, w0)); - a.addWatcher(new CombineWatcher(a, w1)); - a.addWatcher(new CombineWatcher(a, w2)); + a.addObserver(new CombineObserver(a, w0)); + a.addObserver(new CombineObserver(a, w1)); + a.addObserver(new CombineObserver(a, w2)); return a; } - public static IObservable combineLatest(IObservable w0, IObservable w1, IObservable w2, IObservable w3, Func4 combineLatestFunction) { + public static Observable combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Func4 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); - a.addWatcher(new CombineWatcher(a, w0)); - a.addWatcher(new CombineWatcher(a, w1)); - a.addWatcher(new CombineWatcher(a, w2)); - a.addWatcher(new CombineWatcher(a, w3)); + a.addObserver(new CombineObserver(a, w0)); + a.addObserver(new CombineObserver(a, w1)); + a.addObserver(new CombineObserver(a, w2)); + a.addObserver(new CombineObserver(a, w3)); return a; } - private static class CombineWatcher implements IObserver { - final IObservable w; + private static class CombineObserver implements Observer { + final Observable w; final Aggregator a; - private IDisposable subscription; + private Subscription subscription; - public CombineWatcher(Aggregator a, IObservable w) { + public CombineObserver(Aggregator a, Observable w) { this.a = a; this.w = w; } @@ -84,64 +82,64 @@ public void onNext(Object args) { } /** - * Receive notifications from each of the Watchables we are reducing and execute the combineLatestFunction whenever we have received events from all Watchables. + * Receive notifications from each of the Observables we are reducing and execute the combineLatestFunction whenever we have received events from all Observables. * * @param */ - private static class Aggregator extends AbstractIObservable { + private static class Aggregator extends Observable { private final FuncN combineLatestFunction; - private IObserver watcher; + private Observer Observer; private AtomicBoolean running = new AtomicBoolean(true); /** - * Use LinkedHashMap to retain the order we receive the CombineLatestWatcher objects in. + * Use LinkedHashMap to retain the order we receive the CombineLatestObserver objects in. *

          * Note that access to this LinkedList inside MUST BE SYNCHRONIZED */ - private Map, LinkedList> receivedValuesPerWatcher = new LinkedHashMap, LinkedList>(); + private Map, LinkedList> receivedValuesPerObserver = new LinkedHashMap, LinkedList>(); /** - * store when a watcher completes + * store when a Observer completes *

          * Note that access to this set MUST BE SYNCHRONIZED * */ - private HashSet> completed = new HashSet>(); + private HashSet> completed = new HashSet>(); /** - * The last value from a watcher + * The last value from a Observer *

          * Note that access to this set MUST BE SYNCHRONIZED * */ - private HashMap, Object> lastValue = new HashMap, Object>(); + private HashMap, Object> lastValue = new HashMap, Object>(); public Aggregator(FuncN combineLatestFunction) { this.combineLatestFunction = combineLatestFunction; } /** - * Receive notification of a watcher starting (meaning we should require it for aggregation) + * Receive notification of a Observer starting (meaning we should require it for aggregation) * * @param w */ - synchronized void addWatcher(CombineWatcher w) { - // initialize this CombineLatestWatcher - receivedValuesPerWatcher.put(w, new LinkedList()); + synchronized void addObserver(CombineObserver w) { + // initialize this CombineLatestObserver + receivedValuesPerObserver.put(w, new LinkedList()); } /** - * Receive notification of a watcher completing its iterations. + * Receive notification of a Observer completing its iterations. * * @param w */ - synchronized void complete(CombineWatcher w) { - // store that this ZipWatcher is completed + synchronized void complete(CombineObserver w) { + // store that this ZipObserver is completed completed.add(w); - // if all CombineWatchers are completed, we mark the whole thing as completed - if (completed.size() == receivedValuesPerWatcher.size()) { + // if all CombineObservers are completed, we mark the whole thing as completed + if (completed.size() == receivedValuesPerObserver.size()) { if (running.get()) { // mark ourselves as done - watcher.onCompleted(); + Observer.onCompleted(); // just to ensure we stop processing in case we receive more onNext/complete/error calls after this running.set(false); } @@ -149,29 +147,29 @@ synchronized void complete(CombineWatcher w) { } /** - * Receive error for a watcher. Throw the error up the chain and stop processing. + * Receive error for a Observer. Throw the error up the chain and stop processing. * * @param w */ - synchronized void error(CombineWatcher w, Exception e) { - watcher.onError(e); - /* tell ourselves to stop processing onNext events, event if the watchers don't obey the unsubscribe we're about to send */ + synchronized void error(CombineObserver w, Exception e) { + Observer.onError(e); + /* tell ourselves to stop processing onNext events, event if the Observers don't obey the unsubscribe we're about to send */ running.set(false); - /* tell all watchers to unsubscribe since we had an error */ + /* tell all Observers to unsubscribe since we had an error */ stop(); } /** - * Receive the next value from a watcher. + * Receive the next value from a Observer. *

          - * If we have received values from all watchers, trigger the combineLatest function, otherwise store the value and keep waiting. + * If we have received values from all Observers, trigger the combineLatest function, otherwise store the value and keep waiting. * * @param w * @param arg */ - void next(CombineWatcher w, Object arg) { - if (watcher == null) { - throw new RuntimeException("This shouldn't be running if a watcher isn't registered"); + void next(CombineObserver w, Object arg) { + if (Observer == null) { + throw new RuntimeException("This shouldn't be running if a Observer isn't registered"); } /* if we've been 'unsubscribed' don't process anything further even if the things we're watching keep sending (likely because they are not responding to the unsubscribe call) */ @@ -180,37 +178,37 @@ void next(CombineWatcher w, Object arg) { } // define here so the variable is out of the synchronized scope - Object[] argsToCombineLatest = new Object[receivedValuesPerWatcher.size()]; + Object[] argsToCombineLatest = new Object[receivedValuesPerObserver.size()]; // we synchronize everything that touches receivedValues and the internal LinkedList objects synchronized (this) { - // add this value to the queue of the CombineLatestWatcher for values received - receivedValuesPerWatcher.get(w).add(arg); - // remember this as the last value for this Watcher + // add this value to the queue of the CombineLatestObserver for values received + receivedValuesPerObserver.get(w).add(arg); + // remember this as the last value for this Observer lastValue.put(w, arg); - // if all CombineLatestWatchers in 'receivedValues' map have a value, invoke the combineLatestFunction - for (CombineWatcher rw : receivedValuesPerWatcher.keySet()) { - if (receivedValuesPerWatcher.get(rw).peek() == null && !completed.contains(rw)) { - // we have a null (and the watcher isn't completed) meaning the queues aren't all populated so won't do anything + // if all CombineLatestObservers in 'receivedValues' map have a value, invoke the combineLatestFunction + for (CombineObserver rw : receivedValuesPerObserver.keySet()) { + if (receivedValuesPerObserver.get(rw).peek() == null && !completed.contains(rw)) { + // we have a null (and the Observer isn't completed) meaning the queues aren't all populated so won't do anything return; } } // if we get to here this means all the queues have data (or some are completed) int i = 0; boolean foundData = false; - for (CombineWatcher _w : receivedValuesPerWatcher.keySet()) { - LinkedList q = receivedValuesPerWatcher.get(_w); + for (CombineObserver _w : receivedValuesPerObserver.keySet()) { + LinkedList q = receivedValuesPerObserver.get(_w); if (q.peek() == null) { - // this is a completed watcher - // we rely on the check above looking at completed.contains to mean that NULL here represents a completed watcher + // this is a completed Observer + // we rely on the check above looking at completed.contains to mean that NULL here represents a completed Observer argsToCombineLatest[i++] = lastValue.get(_w); } else { foundData = true; argsToCombineLatest[i++] = q.remove(); } } - if (completed.size() == receivedValuesPerWatcher.size() && !foundData) { + if (completed.size() == receivedValuesPerObserver.size() && !foundData) { // all are completed and queues have run out of data, so return and don't send empty data return; } @@ -218,22 +216,22 @@ void next(CombineWatcher w, Object arg) { // if we did not return above from the synchronized block we can now invoke the combineLatestFunction with all of the args // we do this outside the synchronized block as it is now safe to call this concurrently and don't need to block other threads from calling // this 'next' method while another thread finishes calling this combineLatestFunction - watcher.onNext(combineLatestFunction.call(argsToCombineLatest)); + Observer.onNext(combineLatestFunction.call(argsToCombineLatest)); } @Override - public IDisposable subscribe(IObserver watcher) { - if (this.watcher != null) { - throw new IllegalStateException("Only one watcher can subscribe to this Watchable."); + public Subscription subscribe(Observer Observer) { + if (this.Observer != null) { + throw new IllegalStateException("Only one Observer can subscribe to this Observable."); } - this.watcher = watcher; + this.Observer = Observer; - /* start the watchers */ - for (CombineWatcher rw : receivedValuesPerWatcher.keySet()) { + /* start the Observers */ + for (CombineObserver rw : receivedValuesPerObserver.keySet()) { rw.startWatching(); } - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -246,8 +244,8 @@ public void unsubscribe() { private void stop() { /* tell ourselves to stop processing onNext events */ running.set(false); - /* propogate to all watchers to unsubscribe */ - for (CombineWatcher rw : receivedValuesPerWatcher.keySet()) { + /* propogate to all Observers to unsubscribe */ + for (CombineObserver rw : receivedValuesPerObserver.keySet()) { if (rw.subscription != null) { rw.subscription.unsubscribe(); } @@ -261,32 +259,32 @@ public static class UnitTest { @SuppressWarnings("unchecked") /* mock calls don't do generics */ @Test - public void testCombineLatestDifferentLengthWatchableSequences1() { - IObserver w = mock(IObserver.class); + public void testCombineLatestDifferentLengthObservableSequences1() { + Observer w = mock(Observer.class); - TestWatchable w1 = new TestWatchable(); - TestWatchable w2 = new TestWatchable(); - TestWatchable w3 = new TestWatchable(); + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); - IObservable combineLatestW = combineLatest(w1, w2, w3, getConcat3StringsCombineLatestFunction()); + Observable combineLatestW = combineLatest(w1, w2, w3, getConcat3StringsCombineLatestFunction()); combineLatestW.subscribe(w); /* simulate sending data */ // once for w1 - w1.watcher.onNext("1a"); - w1.watcher.onCompleted(); + w1.Observer.onNext("1a"); + w1.Observer.onCompleted(); // twice for w2 - w2.watcher.onNext("2a"); - w2.watcher.onNext("2b"); - w2.watcher.onCompleted(); + w2.Observer.onNext("2a"); + w2.Observer.onNext("2b"); + w2.Observer.onCompleted(); // 4 times for w3 - w3.watcher.onNext("3a"); - w3.watcher.onNext("3b"); - w3.watcher.onNext("3c"); - w3.watcher.onNext("3d"); - w3.watcher.onCompleted(); + w3.Observer.onNext("3a"); + w3.Observer.onNext("3b"); + w3.Observer.onNext("3c"); + w3.Observer.onNext("3d"); + w3.Observer.onCompleted(); - /* we should have been called 4 times on the watcher */ + /* we should have been called 4 times on the Observer */ InOrder inOrder = inOrder(w); inOrder.verify(w).onNext("1a2a3a"); inOrder.verify(w).onNext("1a2b3b"); @@ -297,33 +295,33 @@ public void testCombineLatestDifferentLengthWatchableSequences1() { } @Test - public void testCombineLatestDifferentLengthWatchableSequences2() { + public void testCombineLatestDifferentLengthObservableSequences2() { @SuppressWarnings("unchecked") - IObserver w = mock(IObserver.class); + Observer w = mock(Observer.class); - TestWatchable w1 = new TestWatchable(); - TestWatchable w2 = new TestWatchable(); - TestWatchable w3 = new TestWatchable(); + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); - IObservable combineLatestW = combineLatest(w1, w2, w3, getConcat3StringsCombineLatestFunction()); + Observable combineLatestW = combineLatest(w1, w2, w3, getConcat3StringsCombineLatestFunction()); combineLatestW.subscribe(w); /* simulate sending data */ // 4 times for w1 - w1.watcher.onNext("1a"); - w1.watcher.onNext("1b"); - w1.watcher.onNext("1c"); - w1.watcher.onNext("1d"); - w1.watcher.onCompleted(); + w1.Observer.onNext("1a"); + w1.Observer.onNext("1b"); + w1.Observer.onNext("1c"); + w1.Observer.onNext("1d"); + w1.Observer.onCompleted(); // twice for w2 - w2.watcher.onNext("2a"); - w2.watcher.onNext("2b"); - w2.watcher.onCompleted(); + w2.Observer.onNext("2a"); + w2.Observer.onNext("2b"); + w2.Observer.onCompleted(); // 1 times for w3 - w3.watcher.onNext("3a"); - w3.watcher.onCompleted(); + w3.Observer.onNext("3a"); + w3.Observer.onCompleted(); - /* we should have been called 1 time only on the watcher since we only combine the "latest" we don't go back and loop through others once completed */ + /* we should have been called 1 time only on the Observer since we only combine the "latest" we don't go back and loop through others once completed */ InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("1a2a3a"); inOrder.verify(w, never()).onNext(anyString()); @@ -340,43 +338,43 @@ public void testCombineLatestDifferentLengthWatchableSequences2() { @Test public void testAggregatorSimple() { FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all watchables provide values */ + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ Aggregator a = new Aggregator(combineLatestFunction); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - CombineWatcher r1 = mock(CombineWatcher.class); - CombineWatcher r2 = mock(CombineWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, "world"); - InOrder inOrder = inOrder(aWatcher); + InOrder inOrder = inOrder(aObserver); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - inOrder.verify(aWatcher, times(1)).onNext("helloworld"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); a.next(r1, "hello "); a.next(r2, "again"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - inOrder.verify(aWatcher, times(1)).onNext("hello again"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("hello again"); a.complete(r1); a.complete(r2); - inOrder.verify(aWatcher, never()).onNext(anyString()); - verify(aWatcher, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); + verify(aObserver, times(1)).onCompleted(); } @SuppressWarnings("unchecked") @@ -384,36 +382,36 @@ public void testAggregatorSimple() { @Test public void testAggregatorDifferentSizedResultsWithOnComplete() { FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all watchables provide values */ + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ Aggregator a = new Aggregator(combineLatestFunction); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - CombineWatcher r1 = mock(CombineWatcher.class); - CombineWatcher r2 = mock(CombineWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, "world"); a.complete(r2); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("helloworld"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); a.next(r1, "hi"); a.complete(r1); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - verify(aWatcher, times(1)).onNext("hiworld"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("hiworld"); } @SuppressWarnings("unchecked") @@ -421,36 +419,36 @@ public void testAggregatorDifferentSizedResultsWithOnComplete() { @Test public void testAggregateMultipleTypes() { FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all watchables provide values */ + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ Aggregator a = new Aggregator(combineLatestFunction); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - CombineWatcher r1 = mock(CombineWatcher.class); - CombineWatcher r2 = mock(CombineWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, "world"); a.complete(r2); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("helloworld"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); a.next(r1, "hi"); a.complete(r1); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - verify(aWatcher, times(1)).onNext("hiworld"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("hiworld"); } @SuppressWarnings("unchecked") @@ -458,31 +456,31 @@ public void testAggregateMultipleTypes() { @Test public void testAggregate3Types() { FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all watchables provide values */ + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ Aggregator a = new Aggregator(combineLatestFunction); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - CombineWatcher r1 = mock(CombineWatcher.class); - CombineWatcher r2 = mock(CombineWatcher.class); - CombineWatcher r3 = mock(CombineWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); + CombineObserver r3 = mock(CombineObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); - a.addWatcher(r3); + a.addObserver(r1); + a.addObserver(r2); + a.addObserver(r3); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, 2); a.next(r3, new int[] { 5, 6, 7 }); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("hello2[5, 6, 7]"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("hello2[5, 6, 7]"); } @SuppressWarnings("unchecked") @@ -490,45 +488,45 @@ public void testAggregate3Types() { @Test public void testAggregatorsWithDifferentSizesAndTiming() { FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all watchables provide values */ + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ Aggregator a = new Aggregator(combineLatestFunction); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - CombineWatcher r1 = mock(CombineWatcher.class); - CombineWatcher r2 = mock(CombineWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "one"); a.next(r1, "two"); a.next(r1, "three"); a.next(r2, "A"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("oneA"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("oneA"); a.next(r1, "four"); a.complete(r1); a.next(r2, "B"); - verify(aWatcher, times(1)).onNext("twoB"); + verify(aObserver, times(1)).onNext("twoB"); a.next(r2, "C"); - verify(aWatcher, times(1)).onNext("threeC"); + verify(aObserver, times(1)).onNext("threeC"); a.next(r2, "D"); - verify(aWatcher, times(1)).onNext("fourD"); + verify(aObserver, times(1)).onNext("fourD"); a.next(r2, "E"); - verify(aWatcher, times(1)).onNext("fourE"); + verify(aObserver, times(1)).onNext("fourE"); a.complete(r2); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); } @SuppressWarnings("unchecked") @@ -536,37 +534,37 @@ public void testAggregatorsWithDifferentSizesAndTiming() { @Test public void testAggregatorError() { FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all watchables provide values */ + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ Aggregator a = new Aggregator(combineLatestFunction); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - CombineWatcher r1 = mock(CombineWatcher.class); - CombineWatcher r2 = mock(CombineWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, "world"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("helloworld"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); a.error(r1, new RuntimeException("")); a.next(r1, "hello"); a.next(r2, "again"); - verify(aWatcher, times(1)).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); + verify(aObserver, times(1)).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); // we don't want to be called again after an error - verify(aWatcher, times(0)).onNext("helloagain"); + verify(aObserver, times(0)).onNext("helloagain"); } @SuppressWarnings("unchecked") @@ -574,37 +572,37 @@ public void testAggregatorError() { @Test public void testAggregatorUnsubscribe() { FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all watchables provide values */ + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ Aggregator a = new Aggregator(combineLatestFunction); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - IDisposable subscription = a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Subscription subscription = a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - CombineWatcher r1 = mock(CombineWatcher.class); - CombineWatcher r2 = mock(CombineWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, "world"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("helloworld"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); subscription.unsubscribe(); a.next(r1, "hello"); a.next(r2, "again"); - verify(aWatcher, times(0)).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); + verify(aObserver, times(0)).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); // we don't want to be called again after an error - verify(aWatcher, times(0)).onNext("helloagain"); + verify(aObserver, times(0)).onNext("helloagain"); } @SuppressWarnings("unchecked") @@ -612,37 +610,37 @@ public void testAggregatorUnsubscribe() { @Test public void testAggregatorEarlyCompletion() { FuncN combineLatestFunction = getConcatCombineLatestFunction(); - /* create the aggregator which will execute the combineLatest function when all watchables provide values */ + /* create the aggregator which will execute the combineLatest function when all Observables provide values */ Aggregator a = new Aggregator(combineLatestFunction); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - CombineWatcher r1 = mock(CombineWatcher.class); - CombineWatcher r2 = mock(CombineWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + CombineObserver r1 = mock(CombineObserver.class); + CombineObserver r2 = mock(CombineObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "one"); a.next(r1, "two"); a.complete(r1); a.next(r2, "A"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("oneA"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("oneA"); a.complete(r2); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); // we shouldn't get this since completed is called before any other onNext calls could trigger this - verify(aWatcher, never()).onNext("twoA"); + verify(aObserver, never()).onNext("twoA"); } @SuppressWarnings("unchecked") @@ -651,17 +649,17 @@ public void testAggregatorEarlyCompletion() { public void testCombineLatest2Types() { Func2 combineLatestFunction = getConcatStringIntegerCombineLatestFunction(); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); - IObservable w = combineLatest(WatchableExtensions.toWatchable("one", "two"), WatchableExtensions.toWatchable(2, 3, 4), combineLatestFunction); - w.subscribe(aWatcher); + Observable w = combineLatest(Observable.toObservable("one", "two"), Observable.toObservable(2, 3, 4), combineLatestFunction); + w.subscribe(aObserver); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - verify(aWatcher, times(1)).onNext("one2"); - verify(aWatcher, times(1)).onNext("two3"); - verify(aWatcher, times(1)).onNext("two4"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one2"); + verify(aObserver, times(1)).onNext("two3"); + verify(aObserver, times(1)).onNext("two4"); } @SuppressWarnings("unchecked") @@ -670,15 +668,15 @@ public void testCombineLatest2Types() { public void testCombineLatest3TypesA() { Func3 combineLatestFunction = getConcatStringIntegerIntArrayCombineLatestFunction(); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); - IObservable w = combineLatest(WatchableExtensions.toWatchable("one", "two"), WatchableExtensions.toWatchable(2), WatchableExtensions.toWatchable(new int[] { 4, 5, 6 }), combineLatestFunction); - w.subscribe(aWatcher); + Observable w = combineLatest(Observable.toObservable("one", "two"), Observable.toObservable(2), Observable.toObservable(new int[] { 4, 5, 6 }), combineLatestFunction); + w.subscribe(aObserver); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - verify(aWatcher, times(1)).onNext("one2[4, 5, 6]"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one2[4, 5, 6]"); } @SuppressWarnings("unchecked") @@ -687,16 +685,16 @@ public void testCombineLatest3TypesA() { public void testCombineLatest3TypesB() { Func3 combineLatestFunction = getConcatStringIntegerIntArrayCombineLatestFunction(); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); - IObservable w = combineLatest(WatchableExtensions.toWatchable("one"), WatchableExtensions.toWatchable(2), WatchableExtensions.toWatchable(new int[] { 4, 5, 6 }, new int[] { 7, 8 }), combineLatestFunction); - w.subscribe(aWatcher); + Observable w = combineLatest(Observable.toObservable("one"), Observable.toObservable(2), Observable.toObservable(new int[] { 4, 5, 6 }, new int[] { 7, 8 }), combineLatestFunction); + w.subscribe(aObserver); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - verify(aWatcher, times(1)).onNext("one2[4, 5, 6]"); - verify(aWatcher, times(1)).onNext("one2[7, 8]"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one2[4, 5, 6]"); + verify(aObserver, times(1)).onNext("one2[7, 8]"); } private Func3 getConcat3StringsCombineLatestFunction() { @@ -775,15 +773,15 @@ private static String getStringValue(Object o) { } } - private static class TestWatchable extends AbstractIObservable { + private static class TestObservable extends Observable { - IObserver watcher; + Observer Observer; @Override - public IDisposable subscribe(IObserver watcher) { + public Subscription subscribe(Observer Observer) { // just store the variable where it can be accessed so we can manually trigger it - this.watcher = watcher; - return WatchableExtensions.noOpSubscription(); + this.Observer = Observer; + return Observable.noOpSubscription(); } } diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationFilter.java b/rxjava-core/src/main/java/org/rx/operations/OperationFilter.java index 92ea05627b..5421a0a0d4 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationFilter.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationFilter.java @@ -4,56 +4,63 @@ import static org.mockito.Mockito.*; import org.junit.Test; +import org.mockito.Mockito; import org.rx.functions.Func1; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; +public final class OperationFilter { -/* package */final class OperationFilter extends AbstractIObservable { - private final IObservable that; - private final Func1 predicate; - - OperationFilter(IObservable that, Func1 predicate) { - this.that = that; - this.predicate = predicate; + public static Observable filter(Observable that, Func1 predicate) { + return new Filter(that, predicate); } - public IDisposable subscribe(IObserver watcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - final IObserver observer = new AtomicWatcher(watcher, subscription); + private static class Filter extends Observable { + + private final Observable that; + private final Func1 predicate; + + public Filter(Observable that, Func1 predicate) { + this.that = that; + this.predicate = predicate; + } + + public Subscription subscribe(Observer Observer) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + final Observer observer = new AtomicObserver(Observer, subscription); - subscription.setActual(that.subscribe(new IObserver() { - public void onNext(T value) { - try { - if ((boolean) predicate.call(value)) { - observer.onNext(value); + subscription.setActual(that.subscribe(new Observer() { + public void onNext(T value) { + try { + if ((boolean) predicate.call(value)) { + observer.onNext(value); + } + } catch (Exception ex) { + observer.onError(ex); + subscription.unsubscribe(); } - } catch (Exception ex) { - observer.onError(ex); - subscription.unsubscribe(); } - } - public void onError(Exception ex) { - observer.onError(ex); - } + public void onError(Exception ex) { + observer.onError(ex); + } - public void onCompleted() { - observer.onCompleted(); - } - })); + public void onCompleted() { + observer.onCompleted(); + } + })); - return subscription; + return subscription; + } } public static class UnitTest { @Test public void testFilter() { - IObservable w = WatchableExtensions.toWatchable("one", "two", "three"); - IObservable watchable = new OperationFilter(w, new Func1() { + Observable w = Observable.toObservable("one", "two", "three"); + Observable Observable = filter(w, new Func1() { @Override public Boolean call(String t1) { @@ -65,13 +72,13 @@ public Boolean call(String t1) { }); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); - verify(aWatcher, never()).onNext("one"); - verify(aWatcher, times(1)).onNext("two"); - verify(aWatcher, never()).onNext("three"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + Observer aObserver = mock(Observer.class); + Observable.subscribe(aObserver); + verify(aObserver, Mockito.never()).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); } } } \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationLast.java b/rxjava-core/src/main/java/org/rx/operations/OperationLast.java index 6b3db2def1..b412c1d76e 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationLast.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationLast.java @@ -7,66 +7,73 @@ import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - +import org.mockito.Mockito; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; /** * Returns the last element of an observable sequence. * * @param */ -/* package */final class OperationLast extends AbstractIObservable { - private final AtomicReference lastValue = new AtomicReference(); - private final IObservable that; - private final AtomicBoolean onNextCalled = new AtomicBoolean(false); +public final class OperationLast { - OperationLast(IObservable that) { - this.that = that; + public static Observable last(Observable observable) { + return new Last(observable); } - public IDisposable subscribe(final IObserver watcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - final IObserver observer = new AtomicWatcher(watcher, subscription); + private static class Last extends Observable { + + private final AtomicReference lastValue = new AtomicReference(); + private final Observable that; + private final AtomicBoolean onNextCalled = new AtomicBoolean(false); + + public Last(Observable that) { + this.that = that; + } + + public Subscription subscribe(final Observer Observer) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + final Observer observer = new AtomicObserver(Observer, subscription); - subscription.setActual(that.subscribe(new IObserver() { - public void onNext(T value) { - onNextCalled.set(true); - lastValue.set(value); - } + subscription.setActual(that.subscribe(new Observer() { + public void onNext(T value) { + onNextCalled.set(true); + lastValue.set(value); + } - public void onError(Exception ex) { - observer.onError(ex); - } + public void onError(Exception ex) { + observer.onError(ex); + } - public void onCompleted() { - if (onNextCalled.get()) { - observer.onNext(lastValue.get()); + public void onCompleted() { + if (onNextCalled.get()) { + observer.onNext(lastValue.get()); + } + observer.onCompleted(); } - observer.onCompleted(); - } - })); + })); - return subscription; + return subscription; + } } public static class UnitTest { @Test public void testLast() { - IObservable w = WatchableExtensions.toWatchable("one", "two", "three"); - IObservable watchable = new OperationLast(w); + Observable w = Observable.toObservable("one", "two", "three"); + Observable Observable = last(w); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); - verify(aWatcher, never()).onNext("one"); - verify(aWatcher, never()).onNext("two"); - verify(aWatcher, times(1)).onNext("three"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + Observer aObserver = mock(Observer.class); + Observable.subscribe(aObserver); + verify(aObserver, Mockito.never()).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); } } } \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationMap.java b/rxjava-core/src/main/java/org/rx/operations/OperationMap.java index 9b7b923203..18b17ac762 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationMap.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationMap.java @@ -11,13 +11,11 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.rx.functions.Func1; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; - -/* package */class OperationMap { +public final class OperationMap { /** * Accepts a sequence and a transformation function. Returns a sequence that is the result of @@ -33,7 +31,7 @@ * the type of the output sequence. * @return a sequence that is the result of applying the transformation function to each item in the input sequence. */ - public static IObservable map(IObservable sequence, Func1 func) { + public static Observable map(Observable sequence, Func1 func) { return new MapObservable(sequence, func); } @@ -41,7 +39,7 @@ public static IObservable map(IObservable sequence, Func1 fun * Accepts a sequence of observable sequences and a transformation function. Returns a flattened sequence that is the result of * applying the transformation function to each item in the sequence of each observable sequence. *

          - * The closure should return an IObservable which will then be merged. + * The closure should return an Observable which will then be merged. * * @param sequence * the input sequence. @@ -53,7 +51,7 @@ public static IObservable map(IObservable sequence, Func1 fun * the type of the output sequence. * @return a sequence that is the result of applying the transformation function to each item in the input sequence. */ - public static IObservable mapMany(IObservable sequence, Func1, T> func) { + public static Observable mapMany(Observable sequence, Func1, T> func) { return OperationMerge.merge(map(sequence, func)); } @@ -65,19 +63,19 @@ public static IObservable mapMany(IObservable sequence, Func1 * the type of the output sequence. */ - private static class MapObservable extends AbstractIObservable { - public MapObservable(IObservable sequence, Func1 func) { + private static class MapObservable extends Observable { + public MapObservable(Observable sequence, Func1 func) { this.sequence = sequence; this.func = func; } - private IObservable sequence; + private Observable sequence; private Func1 func; - public IDisposable subscribe(IObserver watcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - final IObserver observer = new AtomicWatcher(watcher, subscription); + public Subscription subscribe(Observer Observer) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + final Observer observer = new AtomicObserver(Observer, subscription); subscription.setActual(sequence.subscribe(new MapObserver(observer, func))); return subscription; } @@ -91,13 +89,13 @@ public IDisposable subscribe(IObserver watcher) { * @param * the type of the inner observer items. */ - private static class MapObserver implements IObserver { - public MapObserver(IObserver observer, Func1 func) { + private static class MapObserver implements Observer { + public MapObserver(Observer observer, Func1 func) { this.observer = observer; this.func = func; } - IObserver observer; + Observer observer; Func1 func; @@ -120,7 +118,7 @@ public void onCompleted() { public static class UnitTest { @Mock - IObserver stringObserver; + Observer stringObserver; @Before public void before() { @@ -132,9 +130,9 @@ public void testMap() { Map m1 = getMap("One"); Map m2 = getMap("Two"); @SuppressWarnings("unchecked") - IObservable> observable = WatchableExtensions.toWatchable(m1, m2); + Observable> observable = Observable.toObservable(m1, m2); - IObservable m = map(observable, new Func1>() { + Observable m = map(observable, new Func1>() { @Override public String call(Map map) { @@ -154,24 +152,24 @@ public String call(Map map) { @Test public void testMapMany() { /* simulate a top-level async call which returns IDs */ - IObservable ids = WatchableExtensions.toWatchable(1, 2); + Observable ids = Observable.toObservable(1, 2); /* now simulate the behavior to take those IDs and perform nested async calls based on them */ - IObservable m = mapMany(ids, new Func1, Integer>() { + Observable m = mapMany(ids, new Func1, Integer>() { @SuppressWarnings("unchecked") @Override - public IObservable call(Integer id) { - /* simulate making a nested async call which creates another IObservable */ - IObservable> subObservable = null; + public Observable call(Integer id) { + /* simulate making a nested async call which creates another Observable */ + Observable> subObservable = null; if (id == 1) { Map m1 = getMap("One"); Map m2 = getMap("Two"); - subObservable = WatchableExtensions.toWatchable(m1, m2); + subObservable = Observable.toObservable(m1, m2); } else { Map m3 = getMap("Three"); Map m4 = getMap("Four"); - subObservable = WatchableExtensions.toWatchable(m3, m4); + subObservable = Observable.toObservable(m3, m4); } /* simulate kicking off the async call and performing a select on it to transform the data */ @@ -199,20 +197,20 @@ public void testMapMany2() { Map m1 = getMap("One"); Map m2 = getMap("Two"); @SuppressWarnings("unchecked") - IObservable> observable1 = WatchableExtensions.toWatchable(m1, m2); + Observable> observable1 = Observable.toObservable(m1, m2); Map m3 = getMap("Three"); Map m4 = getMap("Four"); @SuppressWarnings("unchecked") - IObservable> observable2 = WatchableExtensions.toWatchable(m3, m4); + Observable> observable2 = Observable.toObservable(m3, m4); @SuppressWarnings("unchecked") - IObservable>> observable = WatchableExtensions.toWatchable(observable1, observable2); + Observable>> observable = Observable.toObservable(observable1, observable2); - IObservable m = mapMany(observable, new Func1, IObservable>>() { + Observable m = mapMany(observable, new Func1, Observable>>() { @Override - public IObservable call(IObservable> o) { + public Observable call(Observable> o) { return map(o, new Func1>() { @Override diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationMaterialize.java b/rxjava-core/src/main/java/org/rx/operations/OperationMaterialize.java index 061fe7d2ae..0964829bf7 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationMaterialize.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationMaterialize.java @@ -6,21 +6,19 @@ import java.util.Vector; import org.junit.Test; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; import org.rx.reactive.Notification; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; /** * Materializes the implicit notifications of an observable sequence as explicit notification values. *

          - * In other words, converts a sequence of OnNext, OnError and OnCompleted events into a sequence of WatchableNotifications containing the OnNext, OnError and OnCompleted values. + * In other words, converts a sequence of OnNext, OnError and OnCompleted events into a sequence of ObservableNotifications containing the OnNext, OnError and OnCompleted values. *

          * See http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx for the Microsoft Rx equivalent. */ -public class OperationMaterialize { +public final class OperationMaterialize { /** * Materializes the implicit notifications of an observable sequence as explicit notification values. @@ -30,40 +28,40 @@ public class OperationMaterialize { * @return An observable sequence whose elements are the result of materializing the notifications of the given sequence. * @see http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx */ - public static IObservable> materialize(final IObservable sequence) { - return new MaterializeWatchable(sequence); + public static Observable> materialize(final Observable sequence) { + return new MaterializeObservable(sequence); } - private static class MaterializeWatchable extends AbstractIObservable> { + private static class MaterializeObservable extends Observable> { - private final IObservable sequence; + private final Observable sequence; - public MaterializeWatchable(IObservable sequence) { + public MaterializeObservable(Observable sequence) { this.sequence = sequence; } @Override - public IDisposable subscribe(IObserver> watcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - final IObserver> atomicWatcher = new AtomicWatcher>(watcher, subscription); + public Subscription subscribe(Observer> Observer) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + final Observer> atomicObserver = new AtomicObserver>(Observer, subscription); - subscription.setActual(sequence.subscribe(new IObserver() { + subscription.setActual(sequence.subscribe(new Observer() { @Override public void onCompleted() { - atomicWatcher.onNext(new Notification()); - atomicWatcher.onCompleted(); + atomicObserver.onNext(new Notification()); + atomicObserver.onCompleted(); } @Override public void onError(Exception e) { - atomicWatcher.onNext(new Notification(e)); - atomicWatcher.onCompleted(); + atomicObserver.onNext(new Notification(e)); + atomicObserver.onCompleted(); } @Override public void onNext(T value) { - atomicWatcher.onNext(new Notification(value)); + atomicObserver.onNext(new Notification(value)); } })); @@ -77,11 +75,11 @@ public static class UnitTest { @Test public void testMaterialize1() { // null will cause onError to be triggered before "three" can be returned - final TestAsyncErrorWatchable o1 = new TestAsyncErrorWatchable("one", "two", null, "three"); + final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", null, "three"); - TestWatcher watcher = new TestWatcher(); - IObservable> m = materialize(o1); - m.subscribe(watcher); + TestObserver Observer = new TestObserver(); + Observable> m = materialize(o1); + m.subscribe(Observer); try { o1.t.join(); @@ -89,24 +87,24 @@ public void testMaterialize1() { throw new RuntimeException(e); } - assertFalse(watcher.onError); - assertTrue(watcher.onCompleted); - assertEquals(3, watcher.notifications.size()); - assertEquals("one", watcher.notifications.get(0).getValue()); - assertTrue(watcher.notifications.get(0).isOnNext()); - assertEquals("two", watcher.notifications.get(1).getValue()); - assertTrue(watcher.notifications.get(1).isOnNext()); - assertEquals(NullPointerException.class, watcher.notifications.get(2).getException().getClass()); - assertTrue(watcher.notifications.get(2).isOnError()); + assertFalse(Observer.onError); + assertTrue(Observer.onCompleted); + assertEquals(3, Observer.notifications.size()); + assertEquals("one", Observer.notifications.get(0).getValue()); + assertTrue(Observer.notifications.get(0).isOnNext()); + assertEquals("two", Observer.notifications.get(1).getValue()); + assertTrue(Observer.notifications.get(1).isOnNext()); + assertEquals(NullPointerException.class, Observer.notifications.get(2).getException().getClass()); + assertTrue(Observer.notifications.get(2).isOnError()); } @Test public void testMaterialize2() { - final TestAsyncErrorWatchable o1 = new TestAsyncErrorWatchable("one", "two", "three"); + final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three"); - TestWatcher watcher = new TestWatcher(); - IObservable> m = materialize(o1); - m.subscribe(watcher); + TestObserver Observer = new TestObserver(); + Observable> m = materialize(o1); + m.subscribe(Observer); try { o1.t.join(); @@ -114,29 +112,29 @@ public void testMaterialize2() { throw new RuntimeException(e); } - assertFalse(watcher.onError); - assertTrue(watcher.onCompleted); - assertEquals(4, watcher.notifications.size()); - assertEquals("one", watcher.notifications.get(0).getValue()); - assertTrue(watcher.notifications.get(0).isOnNext()); - assertEquals("two", watcher.notifications.get(1).getValue()); - assertTrue(watcher.notifications.get(1).isOnNext()); - assertEquals("three", watcher.notifications.get(2).getValue()); - assertTrue(watcher.notifications.get(2).isOnNext()); - assertTrue(watcher.notifications.get(3).isOnCompleted()); + assertFalse(Observer.onError); + assertTrue(Observer.onCompleted); + assertEquals(4, Observer.notifications.size()); + assertEquals("one", Observer.notifications.get(0).getValue()); + assertTrue(Observer.notifications.get(0).isOnNext()); + assertEquals("two", Observer.notifications.get(1).getValue()); + assertTrue(Observer.notifications.get(1).isOnNext()); + assertEquals("three", Observer.notifications.get(2).getValue()); + assertTrue(Observer.notifications.get(2).isOnNext()); + assertTrue(Observer.notifications.get(3).isOnCompleted()); } @Test public void testMultipleSubscribes() { - final TestAsyncErrorWatchable o1 = new TestAsyncErrorWatchable("one", "two", null, "three"); + final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", null, "three"); - IObservable> m = materialize(o1); + Observable> m = materialize(o1); - TestWatcher watcher1 = new TestWatcher(); - m.subscribe(watcher1); + TestObserver Observer1 = new TestObserver(); + m.subscribe(Observer1); - TestWatcher watcher2 = new TestWatcher(); - m.subscribe(watcher2); + TestObserver Observer2 = new TestObserver(); + m.subscribe(Observer2); try { o1.t.join(); @@ -144,13 +142,13 @@ public void testMultipleSubscribes() { throw new RuntimeException(e); } - assertEquals(3, watcher1.notifications.size()); - assertEquals(3, watcher2.notifications.size()); + assertEquals(3, Observer1.notifications.size()); + assertEquals(3, Observer2.notifications.size()); } } - private static class TestWatcher implements IObserver> { + private static class TestObserver implements Observer> { boolean onCompleted = false; boolean onError = false; @@ -173,18 +171,18 @@ public void onNext(Notification value) { } - private static class TestAsyncErrorWatchable extends AbstractIObservable { + private static class TestAsyncErrorObservable extends Observable { String[] valuesToReturn; - TestAsyncErrorWatchable(String... values) { + TestAsyncErrorObservable(String... values) { valuesToReturn = values; } Thread t; @Override - public IDisposable subscribe(final IObserver observer) { + public Subscription subscribe(final Observer observer) { t = new Thread(new Runnable() { @Override @@ -210,7 +208,7 @@ public void run() { }); t.start(); - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationMerge.java b/rxjava-core/src/main/java/org/rx/operations/OperationMerge.java index 1447e260dc..acd3106df6 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationMerge.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationMerge.java @@ -13,42 +13,40 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; - -/* package */class OperationMerge { +public final class OperationMerge { /** - * Flattens the observable sequences from the list of IObservables into one observable sequence without any transformation. + * Flattens the observable sequences from the list of Observables into one observable sequence without any transformation. * * @param source * An observable sequence of elements to project. - * @return An observable sequence whose elements are the result of flattening the output from the list of IObservables. + * @return An observable sequence whose elements are the result of flattening the output from the list of Observables. * @see http://msdn.microsoft.com/en-us/library/hh229099(v=vs.103).aspx */ - public static IObservable merge(final IObservable> sequences) { - // wrap in a Watchable so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of merge rather than 1 handling both, which is not thread-safe. - return new AbstractIObservable() { + public static Observable merge(final Observable> sequences) { + // wrap in a Observable so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of merge rather than 1 handling both, which is not thread-safe. + return new Observable() { @Override - public IDisposable subscribe(IObserver watcher) { - AtomicWatchableSubscription s = new AtomicWatchableSubscription(); - s.setActual(new MergeObservable(sequences).subscribe(new AtomicWatcher(watcher, s))); + public Subscription subscribe(Observer Observer) { + AtomicObservableSubscription s = new AtomicObservableSubscription(); + s.setActual(new MergeObservable(sequences).subscribe(new AtomicObserver(Observer, s))); return s; } }; } - public static IObservable merge(final IObservable... sequences) { - return merge(new AbstractIObservable>() { + public static Observable merge(final Observable... sequences) { + return merge(new Observable>() { private volatile boolean unsubscribed = false; @Override - public IDisposable subscribe(IObserver> observer) { - for (IObservable o : sequences) { + public Subscription subscribe(Observer> observer) { + for (Observable o : sequences) { if (!unsubscribed) { observer.onNext(o); } else { @@ -59,7 +57,7 @@ public IDisposable subscribe(IObserver> observer) { if (!unsubscribed) { observer.onCompleted(); } - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -71,14 +69,14 @@ public void unsubscribe() { }); } - public static IObservable merge(final List> sequences) { - return merge(new AbstractIObservable>() { + public static Observable merge(final List> sequences) { + return merge(new Observable>() { private volatile boolean unsubscribed = false; @Override - public IDisposable subscribe(IObserver> observer) { - for (IObservable o : sequences) { + public Subscription subscribe(Observer> observer) { + for (Observable o : sequences) { if (!unsubscribed) { observer.onNext(o); } else { @@ -90,7 +88,7 @@ public IDisposable subscribe(IObserver> observer) { observer.onCompleted(); } - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -109,27 +107,27 @@ public void unsubscribe() { *

          * This should all be fine as long as it's kept as a private class and a new instance created from static factory method above. *

          - * Note how the take() factory method above protects us from a single instance being exposed with the Watchable wrapper handling the subscribe flow. + * Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow. * * @param */ - private static final class MergeObservable extends AbstractIObservable { - private final IObservable> sequences; - private final Subscription ourSubscription = new Subscription(); + private static final class MergeObservable extends Observable { + private final Observable> sequences; + private final MergeSubscription ourSubscription = new MergeSubscription(); private AtomicBoolean stopped = new AtomicBoolean(false); private volatile boolean parentCompleted = false; - private final ConcurrentHashMap childWatchers = new ConcurrentHashMap(); - private final ConcurrentHashMap childSubscriptions = new ConcurrentHashMap(); + private final ConcurrentHashMap childObservers = new ConcurrentHashMap(); + private final ConcurrentHashMap childSubscriptions = new ConcurrentHashMap(); - private MergeObservable(IObservable> sequences) { + private MergeObservable(Observable> sequences) { this.sequences = sequences; } - public IDisposable subscribe(IObserver actualWatcher) { + public MergeSubscription subscribe(Observer actualObserver) { /** - * Subscribe to the parent Watchable to get to the children Watchables + * Subscribe to the parent Observable to get to the children Observables */ - sequences.subscribe(new ParentWatcher(actualWatcher)); + sequences.subscribe(new ParentObserver(actualObserver)); /* return our subscription to allow unsubscribing */ return ourSubscription; @@ -140,7 +138,7 @@ public IDisposable subscribe(IObserver actualWatcher) { *

          * Also has the stop() method returning a boolean so callers know if their thread "won" and should perform further actions. */ - private class Subscription implements IDisposable { + private class MergeSubscription implements Subscription { @Override public void unsubscribe() { @@ -152,7 +150,7 @@ public boolean stop() { boolean didSet = stopped.compareAndSet(false, true); if (didSet) { // this thread won the race to stop, so unsubscribe from the actualSubscription - for (IDisposable _s : childSubscriptions.values()) { + for (Subscription _s : childSubscriptions.values()) { _s.unsubscribe(); } return true; @@ -164,15 +162,15 @@ public boolean stop() { } /** - * Subscribe to the top level Watchable to receive the sequence of Watchable children. + * Subscribe to the top level Observable to receive the sequence of Observable children. * * @param */ - private class ParentWatcher implements IObserver> { - private final IObserver actualWatcher; + private class ParentObserver implements Observer> { + private final Observer actualObserver; - public ParentWatcher(IObserver actualWatcher) { - this.actualWatcher = actualWatcher; + public ParentObserver(Observer actualObserver) { + this.actualObserver = actualObserver; } @Override @@ -182,10 +180,10 @@ public void onCompleted() { // but will let the child worry about it // if however this completes and there are no children processing, then we will send onCompleted - if (childWatchers.size() == 0) { + if (childObservers.size() == 0) { if (!stopped.get()) { if (ourSubscription.stop()) { - actualWatcher.onCompleted(); + actualObserver.onCompleted(); } } } @@ -193,57 +191,57 @@ public void onCompleted() { @Override public void onError(Exception e) { - actualWatcher.onError(e); + actualObserver.onError(e); } @Override - public void onNext(IObservable childWatchable) { + public void onNext(Observable childObservable) { if (stopped.get()) { // we won't act on any further items return; } - if (childWatchable == null) { - throw new IllegalArgumentException("Watchable can not be null."); + if (childObservable == null) { + throw new IllegalArgumentException("Observable can not be null."); } /** - * For each child Watchable we receive we'll subscribe with a separate Watcher - * that will each then forward their sequences to the actualWatcher. + * For each child Observable we receive we'll subscribe with a separate Observer + * that will each then forward their sequences to the actualObserver. *

          - * We use separate child watchers for each sequence to simplify the onComplete/onError handling so each sequence has its own lifecycle. + * We use separate child Observers for each sequence to simplify the onComplete/onError handling so each sequence has its own lifecycle. */ - ChildWatcher _w = new ChildWatcher(actualWatcher); - childWatchers.put(_w, _w); - IDisposable _subscription = childWatchable.subscribe(_w); - // remember this watcher and the subscription from it + ChildObserver _w = new ChildObserver(actualObserver); + childObservers.put(_w, _w); + Subscription _subscription = childObservable.subscribe(_w); + // remember this Observer and the subscription from it childSubscriptions.put(_w, _subscription); } } /** - * Subscribe to each child Watchable and forward their sequence of data to the actualWatcher + * Subscribe to each child Observable and forward their sequence of data to the actualObserver * */ - private class ChildWatcher implements IObserver { + private class ChildObserver implements Observer { - private final IObserver actualWatcher; + private final Observer actualObserver; - public ChildWatcher(IObserver actualWatcher) { - this.actualWatcher = actualWatcher; + public ChildObserver(Observer actualObserver) { + this.actualObserver = actualObserver; } @Override public void onCompleted() { - // remove self from map of watchers - childWatchers.remove(this); - // if there are now 0 watchers left, so if the parent is also completed we send the onComplete to the actualWatcher - // if the parent is not complete that means there is another sequence (and child watcher) to come + // remove self from map of Observers + childObservers.remove(this); + // if there are now 0 Observers left, so if the parent is also completed we send the onComplete to the actualObserver + // if the parent is not complete that means there is another sequence (and child Observer) to come if (!stopped.get()) { - if (childWatchers.size() == 0 && parentCompleted) { + if (childObservers.size() == 0 && parentCompleted) { if (ourSubscription.stop()) { // this thread 'won' the race to unsubscribe/stop so let's send onCompleted - actualWatcher.onCompleted(); + actualObserver.onCompleted(); } } } @@ -254,17 +252,17 @@ public void onError(Exception e) { if (!stopped.get()) { if (ourSubscription.stop()) { // this thread 'won' the race to unsubscribe/stop so let's send the error - actualWatcher.onError(e); + actualObserver.onError(e); } } } @Override public void onNext(T args) { - // in case the Watchable is poorly behaved and doesn't listen to the unsubscribe request + // in case the Observable is poorly behaved and doesn't listen to the unsubscribe request // we'll ignore anything that comes in after we've unsubscribed if (!stopped.get()) { - actualWatcher.onNext(args); + actualObserver.onNext(args); } } @@ -273,7 +271,7 @@ public void onNext(T args) { public static class UnitTest { @Mock - IObserver stringObserver; + Observer stringObserver; @Before public void before() { @@ -282,19 +280,19 @@ public void before() { @Test public void testMergeObservableOfObservables() { - final IObservable o1 = new TestSynchronousWatchable(); - final IObservable o2 = new TestSynchronousWatchable(); + final Observable o1 = new TestSynchronousObservable(); + final Observable o2 = new TestSynchronousObservable(); - IObservable> observableOfObservables = new AbstractIObservable>() { + Observable> observableOfObservables = new Observable>() { @Override - public IDisposable subscribe(IObserver> observer) { + public Subscription subscribe(Observer> observer) { // simulate what would happen in an observable observer.onNext(o1); observer.onNext(o2); observer.onCompleted(); - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -305,7 +303,7 @@ public void unsubscribe() { } }; - IObservable m = merge(observableOfObservables); + Observable m = merge(observableOfObservables); m.subscribe(stringObserver); verify(stringObserver, never()).onError(any(Exception.class)); @@ -315,11 +313,11 @@ public void unsubscribe() { @Test public void testMergeArray() { - final IObservable o1 = new TestSynchronousWatchable(); - final IObservable o2 = new TestSynchronousWatchable(); + final Observable o1 = new TestSynchronousObservable(); + final Observable o2 = new TestSynchronousObservable(); @SuppressWarnings("unchecked") - IObservable m = merge(o1, o2); + Observable m = merge(o1, o2); m.subscribe(stringObserver); verify(stringObserver, never()).onError(any(Exception.class)); @@ -329,13 +327,13 @@ public void testMergeArray() { @Test public void testMergeList() { - final IObservable o1 = new TestSynchronousWatchable(); - final IObservable o2 = new TestSynchronousWatchable(); - List> listOfObservables = new ArrayList>(); + final Observable o1 = new TestSynchronousObservable(); + final Observable o2 = new TestSynchronousObservable(); + List> listOfObservables = new ArrayList>(); listOfObservables.add(o1); listOfObservables.add(o2); - IObservable m = merge(listOfObservables); + Observable m = merge(listOfObservables); m.subscribe(stringObserver); verify(stringObserver, never()).onError(any(Exception.class)); @@ -345,12 +343,12 @@ public void testMergeList() { @Test public void testUnSubscribe() { - TestWatchable tA = new TestWatchable(); - TestWatchable tB = new TestWatchable(); + TestObservable tA = new TestObservable(); + TestObservable tB = new TestObservable(); @SuppressWarnings("unchecked") - IObservable m = merge(tA, tB); - IDisposable s = m.subscribe(stringObserver); + Observable m = merge(tA, tB); + Subscription s = m.subscribe(stringObserver); tA.sendOnNext("Aone"); tB.sendOnNext("Bone"); @@ -372,11 +370,11 @@ public void testUnSubscribe() { @Test public void testMergeArrayWithThreading() { - final TestASynchronousWatchable o1 = new TestASynchronousWatchable(); - final TestASynchronousWatchable o2 = new TestASynchronousWatchable(); + final TestASynchronousObservable o1 = new TestASynchronousObservable(); + final TestASynchronousObservable o2 = new TestASynchronousObservable(); @SuppressWarnings("unchecked") - IObservable m = merge(o1, o2); + Observable m = merge(o1, o2); m.subscribe(stringObserver); try { @@ -397,11 +395,11 @@ public void testMergeArrayWithThreading() { @Test public void testError1() { // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior - final IObservable o1 = new TestErrorWatchable("four", null, "six"); // we expect to lose "six" - final IObservable o2 = new TestErrorWatchable("one", "two", "three"); // we expect to lose all of these since o1 is done first and fails + final Observable o1 = new TestErrorObservable("four", null, "six"); // we expect to lose "six" + final Observable o2 = new TestErrorObservable("one", "two", "three"); // we expect to lose all of these since o1 is done first and fails @SuppressWarnings("unchecked") - IObservable m = merge(o1, o2); + Observable m = merge(o1, o2); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); @@ -420,13 +418,13 @@ public void testError1() { @Test public void testError2() { // we are using synchronous execution to test this exactly rather than non-deterministic concurrent behavior - final IObservable o1 = new TestErrorWatchable("one", "two", "three"); - final IObservable o2 = new TestErrorWatchable("four", null, "six"); // we expect to lose "six" - final IObservable o3 = new TestErrorWatchable("seven", "eight", null);// we expect to lose all of these since o2 is done first and fails - final IObservable o4 = new TestErrorWatchable("nine");// we expect to lose all of these since o2 is done first and fails + final Observable o1 = new TestErrorObservable("one", "two", "three"); + final Observable o2 = new TestErrorObservable("four", null, "six"); // we expect to lose "six" + final Observable o3 = new TestErrorObservable("seven", "eight", null);// we expect to lose all of these since o2 is done first and fails + final Observable o4 = new TestErrorObservable("nine");// we expect to lose all of these since o2 is done first and fails @SuppressWarnings("unchecked") - IObservable m = merge(o1, o2, o3, o4); + Observable m = merge(o1, o2, o3, o4); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); @@ -442,15 +440,15 @@ public void testError2() { verify(stringObserver, times(0)).onNext("nine"); } - private static class TestSynchronousWatchable extends AbstractIObservable { + private static class TestSynchronousObservable extends Observable { @Override - public IDisposable subscribe(IObserver observer) { + public Subscription subscribe(Observer observer) { observer.onNext("hello"); observer.onCompleted(); - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -461,11 +459,11 @@ public void unsubscribe() { } } - private static class TestASynchronousWatchable extends AbstractIObservable { + private static class TestASynchronousObservable extends Observable { Thread t; @Override - public IDisposable subscribe(final IObserver observer) { + public Subscription subscribe(final Observer observer) { t = new Thread(new Runnable() { @Override @@ -477,7 +475,7 @@ public void run() { }); t.start(); - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -489,13 +487,13 @@ public void unsubscribe() { } /** - * A Watchable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. + * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. */ - private static class TestWatchable extends AbstractIObservable { + private static class TestObservable extends Observable { - IObserver observer = null; + Observer observer = null; volatile boolean unsubscribed = false; - IDisposable s = new IDisposable() { + Subscription s = new Subscription() { @Override public void unsubscribe() { @@ -505,7 +503,7 @@ public void unsubscribe() { }; - public TestWatchable() { + public TestObservable() { } /* used to simulate subscription */ @@ -525,22 +523,22 @@ public void sendOnError(Exception e) { } @Override - public IDisposable subscribe(final IObserver observer) { + public Subscription subscribe(final Observer observer) { this.observer = observer; return s; } } - private static class TestErrorWatchable extends AbstractIObservable { + private static class TestErrorObservable extends Observable { String[] valuesToReturn; - TestErrorWatchable(String... values) { + TestErrorObservable(String... values) { valuesToReturn = values; } @Override - public IDisposable subscribe(IObserver observer) { + public Subscription subscribe(Observer observer) { for (String s : valuesToReturn) { if (s == null) { @@ -552,7 +550,7 @@ public IDisposable subscribe(IObserver observer) { } observer.onCompleted(); - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationMergeDelayError.java b/rxjava-core/src/main/java/org/rx/operations/OperationMergeDelayError.java index 7c3238cc3d..da9c145aea 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationMergeDelayError.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationMergeDelayError.java @@ -14,51 +14,51 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.rx.reactive.AbstractIObservable; import org.rx.reactive.CompositeException; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; /** - * Same functionality as OperationMerge except that onError events will be skipped so that all onNext calls are passed on until all sequences finish with onComplete or onError, and then the first onError received (if any) will be passed on. + * Same functionality as OperationMerge except that onError events will be skipped so that all onNext calls are passed on until all sequences finish with onComplete or onError, and then the first + * onError received (if any) will be passed on. *

          * This allows retrieving all successful onNext calls without being blocked by an onError early in a sequence. *

          * NOTE: If this is used on an infinite stream it will never call onError and effectively will swallow errors. */ -/* package */class OperationMergeDelayError { +public final class OperationMergeDelayError { /** - * Flattens the observable sequences from the list of IObservables into one observable sequence without any transformation and delays any onError calls until after all sequences have called onError or onComplete so as to allow all successful + * Flattens the observable sequences from the list of Observables into one observable sequence without any transformation and delays any onError calls until after all sequences have called + * onError or onComplete so as to allow all successful * onNext calls to be received. * * @param source * An observable sequence of elements to project. - * @return An observable sequence whose elements are the result of flattening the output from the list of IObservables. + * @return An observable sequence whose elements are the result of flattening the output from the list of Observables. * @see http://msdn.microsoft.com/en-us/library/hh229099(v=vs.103).aspx */ - public static IObservable mergeDelayError(final IObservable> sequences) { - // wrap in a Watchable so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take rather than 1 handing both, which is not thread-safe. - return new AbstractIObservable() { + public static Observable mergeDelayError(final Observable> sequences) { + // wrap in a Observable so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take rather than 1 handing both, which is not thread-safe. + return new Observable() { @Override - public IDisposable subscribe(IObserver watcher) { - AtomicWatchableSubscription s = new AtomicWatchableSubscription(); - s.setActual(new MergeDelayErrorObservable(sequences).subscribe(new AtomicWatcher(watcher, s))); + public Subscription subscribe(Observer Observer) { + AtomicObservableSubscription s = new AtomicObservableSubscription(); + s.setActual(new MergeDelayErrorObservable(sequences).subscribe(new AtomicObserver(Observer, s))); return s; } }; } - public static IObservable mergeDelayError(final IObservable... sequences) { - return mergeDelayError(new AbstractIObservable>() { + public static Observable mergeDelayError(final Observable... sequences) { + return mergeDelayError(new Observable>() { private volatile boolean unsubscribed = false; @Override - public IDisposable subscribe(IObserver> observer) { - for (IObservable o : sequences) { + public Subscription subscribe(Observer> observer) { + for (Observable o : sequences) { if (!unsubscribed) { observer.onNext(o); } else { @@ -69,7 +69,7 @@ public IDisposable subscribe(IObserver> observer) { if (!unsubscribed) { observer.onCompleted(); } - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -81,14 +81,14 @@ public void unsubscribe() { }); } - public static IObservable mergeDelayError(final List> sequences) { - return mergeDelayError(new AbstractIObservable>() { + public static Observable mergeDelayError(final List> sequences) { + return mergeDelayError(new Observable>() { private volatile boolean unsubscribed = false; @Override - public IDisposable subscribe(IObserver> observer) { - for (IObservable o : sequences) { + public Subscription subscribe(Observer> observer) { + for (Observable o : sequences) { if (!unsubscribed) { observer.onNext(o); } else { @@ -100,7 +100,7 @@ public IDisposable subscribe(IObserver> observer) { observer.onCompleted(); } - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -119,29 +119,29 @@ public void unsubscribe() { *

          * This should all be fine as long as it's kept as a private class and a new instance created from static factory method above. *

          - * Note how the take() factory method above protects us from a single instance being exposed with the Watchable wrapper handling the subscribe flow. + * Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow. * * @param */ - private static final class MergeDelayErrorObservable extends AbstractIObservable { - private final IObservable> sequences; - private final Subscription ourSubscription = new Subscription(); + private static final class MergeDelayErrorObservable extends Observable { + private final Observable> sequences; + private final MergeSubscription ourSubscription = new MergeSubscription(); private AtomicBoolean stopped = new AtomicBoolean(false); private volatile boolean parentCompleted = false; - private final ConcurrentHashMap childWatchers = new ConcurrentHashMap(); - private final ConcurrentHashMap childSubscriptions = new ConcurrentHashMap(); + private final ConcurrentHashMap childObservers = new ConcurrentHashMap(); + private final ConcurrentHashMap childSubscriptions = new ConcurrentHashMap(); // onErrors we received that will be delayed until everything is completed and then sent private ConcurrentLinkedQueue onErrorReceived = new ConcurrentLinkedQueue(); - private MergeDelayErrorObservable(IObservable> sequences) { + private MergeDelayErrorObservable(Observable> sequences) { this.sequences = sequences; } - public IDisposable subscribe(IObserver actualWatcher) { + public Subscription subscribe(Observer actualObserver) { /** - * Subscribe to the parent Watchable to get to the children Watchables + * Subscribe to the parent Observable to get to the children Observables */ - sequences.subscribe(new ParentWatcher(actualWatcher)); + sequences.subscribe(new ParentObserver(actualObserver)); /* return our subscription to allow unsubscribing */ return ourSubscription; @@ -152,7 +152,7 @@ public IDisposable subscribe(IObserver actualWatcher) { *

          * Also has the stop() method returning a boolean so callers know if their thread "won" and should perform further actions. */ - private class Subscription implements IDisposable { + private class MergeSubscription implements Subscription { @Override public void unsubscribe() { @@ -164,7 +164,7 @@ public boolean stop() { boolean didSet = stopped.compareAndSet(false, true); if (didSet) { // this thread won the race to stop, so unsubscribe from the actualSubscription - for (IDisposable _s : childSubscriptions.values()) { + for (Subscription _s : childSubscriptions.values()) { _s.unsubscribe(); } return true; @@ -176,15 +176,15 @@ public boolean stop() { } /** - * Subscribe to the top level Watchable to receive the sequence of Watchable children. + * Subscribe to the top level Observable to receive the sequence of Observable children. * * @param */ - private class ParentWatcher implements IObserver> { - private final IObserver actualWatcher; + private class ParentObserver implements Observer> { + private final Observer actualObserver; - public ParentWatcher(IObserver actualWatcher) { - this.actualWatcher = actualWatcher; + public ParentObserver(Observer actualObserver) { + this.actualObserver = actualObserver; } @Override @@ -194,18 +194,18 @@ public void onCompleted() { // but will let the child worry about it // if however this completes and there are no children processing, then we will send onCompleted - if (childWatchers.size() == 0) { + if (childObservers.size() == 0) { if (!stopped.get()) { if (ourSubscription.stop()) { if (onErrorReceived.size() == 1) { - // an onError was received from 1 ChildWatcher so we now send it as a delayed error - actualWatcher.onError(onErrorReceived.peek()); + // an onError was received from 1 ChildObserver so we now send it as a delayed error + actualObserver.onError(onErrorReceived.peek()); } else if (onErrorReceived.size() > 1) { - // an onError was received from more than 1 ChildWatcher so we now send it as a delayed error - actualWatcher.onError(new CompositeException(onErrorReceived)); + // an onError was received from more than 1 ChildObserver so we now send it as a delayed error + actualObserver.onError(new CompositeException(onErrorReceived)); } else { // no delayed error so send onCompleted - actualWatcher.onCompleted(); + actualObserver.onCompleted(); } } } @@ -214,55 +214,55 @@ public void onCompleted() { @Override public void onError(Exception e) { - actualWatcher.onError(e); + actualObserver.onError(e); } @Override - public void onNext(IObservable childWatchable) { + public void onNext(Observable childObservable) { if (stopped.get()) { // we won't act on any further items return; } - if (childWatchable == null) { - throw new IllegalArgumentException("Watchable can not be null."); + if (childObservable == null) { + throw new IllegalArgumentException("Observable can not be null."); } /** - * For each child Watchable we receive we'll subscribe with a separate Watcher - * that will each then forward their sequences to the actualWatcher. + * For each child Observable we receive we'll subscribe with a separate Observer + * that will each then forward their sequences to the actualObserver. *

          - * We use separate child watchers for each sequence to simplify the onComplete/onError handling so each sequence has its own lifecycle. + * We use separate child Observers for each sequence to simplify the onComplete/onError handling so each sequence has its own lifecycle. */ - ChildWatcher _w = new ChildWatcher(actualWatcher); - childWatchers.put(_w, _w); - IDisposable _subscription = childWatchable.subscribe(_w); - // remember this watcher and the subscription from it + ChildObserver _w = new ChildObserver(actualObserver); + childObservers.put(_w, _w); + Subscription _subscription = childObservable.subscribe(_w); + // remember this Observer and the subscription from it childSubscriptions.put(_w, _subscription); } } /** - * Subscribe to each child Watchable and forward their sequence of data to the actualWatcher + * Subscribe to each child Observable and forward their sequence of data to the actualObserver * */ - private class ChildWatcher implements IObserver { + private class ChildObserver implements Observer { - private final IObserver actualWatcher; + private final Observer actualObserver; private volatile boolean finished = false; - public ChildWatcher(IObserver actualWatcher) { - this.actualWatcher = actualWatcher; + public ChildObserver(Observer actualObserver) { + this.actualObserver = actualObserver; } @Override public void onCompleted() { - // remove self from map of watchers - childWatchers.remove(this); - // if there are now 0 watchers left, so if the parent is also completed we send the onComplete to the actualWatcher - // if the parent is not complete that means there is another sequence (and child watcher) to come + // remove self from map of Observers + childObservers.remove(this); + // if there are now 0 Observers left, so if the parent is also completed we send the onComplete to the actualObserver + // if the parent is not complete that means there is another sequence (and child Observer) to come if (!stopped.get()) { - finishWatcher(); + finishObserver(); } } @@ -270,39 +270,39 @@ public void onCompleted() { public void onError(Exception e) { if (!stopped.get()) { onErrorReceived.add(e); - // mark this ChildWatcher as done - childWatchers.remove(this); - // but do NOT forward to actualWatcher as we want other ChildWatchers to continue until completion + // mark this ChildObserver as done + childObservers.remove(this); + // but do NOT forward to actualObserver as we want other ChildObservers to continue until completion // and we'll delay the sending of onError until all others are done // we mark finished==true as a safety to ensure that if further calls to onNext occur we ignore them finished = true; // check for whether the parent is completed and if so then perform the 'finishing' actions - finishWatcher(); + finishObserver(); } } /** - * onComplete and onError when called need to check for the parent being complete and if so send the onCompleted or onError to the actualWatcher. + * onComplete and onError when called need to check for the parent being complete and if so send the onCompleted or onError to the actualObserver. *

          * This does NOT get invoked if synchronous execution occurs, but will when asynchronously executing. *

          * TestCase testErrorDelayed4WithThreading specifically tests this use case. */ - private void finishWatcher() { - if (childWatchers.size() == 0 && parentCompleted) { + private void finishObserver() { + if (childObservers.size() == 0 && parentCompleted) { if (ourSubscription.stop()) { // this thread 'won' the race to unsubscribe/stop so let's send onError or onCompleted if (onErrorReceived.size() == 1) { - // an onError was received from 1 ChildWatcher so we now send it as a delayed error - actualWatcher.onError(onErrorReceived.peek()); + // an onError was received from 1 ChildObserver so we now send it as a delayed error + actualObserver.onError(onErrorReceived.peek()); } else if (onErrorReceived.size() > 1) { - // an onError was received from more than 1 ChildWatcher so we now send it as a delayed error - actualWatcher.onError(new CompositeException(onErrorReceived)); + // an onError was received from more than 1 ChildObserver so we now send it as a delayed error + actualObserver.onError(new CompositeException(onErrorReceived)); } else { // no delayed error so send onCompleted - actualWatcher.onCompleted(); + actualObserver.onCompleted(); } } } @@ -310,10 +310,10 @@ private void finishWatcher() { @Override public void onNext(T args) { - // in case the Watchable is poorly behaved and doesn't listen to the unsubscribe request + // in case the Observable is poorly behaved and doesn't listen to the unsubscribe request // we'll ignore anything that comes in after we've unsubscribed or an onError has been received and delayed if (!stopped.get() && !finished) { - actualWatcher.onNext(args); + actualObserver.onNext(args); } } @@ -322,7 +322,7 @@ public void onNext(T args) { public static class UnitTest { @Mock - IObserver stringObserver; + Observer stringObserver; @Before public void before() { @@ -331,11 +331,11 @@ public void before() { @Test public void testErrorDelayed1() { - final IObservable o1 = new TestErrorWatchable("four", null, "six"); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final IObservable o2 = new TestErrorWatchable("one", "two", "three"); + final Observable o1 = new TestErrorObservable("four", null, "six"); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o2 = new TestErrorObservable("one", "two", "three"); @SuppressWarnings("unchecked") - IObservable m = mergeDelayError(o1, o2); + Observable m = mergeDelayError(o1, o2); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); @@ -350,13 +350,13 @@ public void testErrorDelayed1() { @Test public void testErrorDelayed2() { - final IObservable o1 = new TestErrorWatchable("one", "two", "three"); - final IObservable o2 = new TestErrorWatchable("four", null, "six"); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final IObservable o3 = new TestErrorWatchable("seven", "eight", null); - final IObservable o4 = new TestErrorWatchable("nine"); + final Observable o1 = new TestErrorObservable("one", "two", "three"); + final Observable o2 = new TestErrorObservable("four", null, "six"); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o3 = new TestErrorObservable("seven", "eight", null); + final Observable o4 = new TestErrorObservable("nine"); @SuppressWarnings("unchecked") - IObservable m = mergeDelayError(o1, o2, o3, o4); + Observable m = mergeDelayError(o1, o2, o3, o4); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); @@ -374,13 +374,13 @@ public void testErrorDelayed2() { @Test public void testErrorDelayed3() { - final IObservable o1 = new TestErrorWatchable("one", "two", "three"); - final IObservable o2 = new TestErrorWatchable("four", "five", "six"); - final IObservable o3 = new TestErrorWatchable("seven", "eight", null); - final IObservable o4 = new TestErrorWatchable("nine"); + final Observable o1 = new TestErrorObservable("one", "two", "three"); + final Observable o2 = new TestErrorObservable("four", "five", "six"); + final Observable o3 = new TestErrorObservable("seven", "eight", null); + final Observable o4 = new TestErrorObservable("nine"); @SuppressWarnings("unchecked") - IObservable m = mergeDelayError(o1, o2, o3, o4); + Observable m = mergeDelayError(o1, o2, o3, o4); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); @@ -398,13 +398,13 @@ public void testErrorDelayed3() { @Test public void testErrorDelayed4() { - final IObservable o1 = new TestErrorWatchable("one", "two", "three"); - final IObservable o2 = new TestErrorWatchable("four", "five", "six"); - final IObservable o3 = new TestErrorWatchable("seven", "eight"); - final IObservable o4 = new TestErrorWatchable("nine", null); + final Observable o1 = new TestErrorObservable("one", "two", "three"); + final Observable o2 = new TestErrorObservable("four", "five", "six"); + final Observable o3 = new TestErrorObservable("seven", "eight"); + final Observable o4 = new TestErrorObservable("nine", null); @SuppressWarnings("unchecked") - IObservable m = mergeDelayError(o1, o2, o3, o4); + Observable m = mergeDelayError(o1, o2, o3, o4); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(NullPointerException.class)); @@ -422,14 +422,14 @@ public void testErrorDelayed4() { @Test public void testErrorDelayed4WithThreading() { - final TestAsyncErrorWatchable o1 = new TestAsyncErrorWatchable("one", "two", "three"); - final TestAsyncErrorWatchable o2 = new TestAsyncErrorWatchable("four", "five", "six"); - final TestAsyncErrorWatchable o3 = new TestAsyncErrorWatchable("seven", "eight"); + final TestAsyncErrorObservable o1 = new TestAsyncErrorObservable("one", "two", "three"); + final TestAsyncErrorObservable o2 = new TestAsyncErrorObservable("four", "five", "six"); + final TestAsyncErrorObservable o3 = new TestAsyncErrorObservable("seven", "eight"); // throw the error at the very end so no onComplete will be called after it - final TestAsyncErrorWatchable o4 = new TestAsyncErrorWatchable("nine", null); + final TestAsyncErrorObservable o4 = new TestAsyncErrorObservable("nine", null); @SuppressWarnings("unchecked") - IObservable m = mergeDelayError(o1, o2, o3, o4); + Observable m = mergeDelayError(o1, o2, o3, o4); m.subscribe(stringObserver); try { @@ -456,11 +456,11 @@ public void testErrorDelayed4WithThreading() { @Test public void testCompositeErrorDelayed1() { - final IObservable o1 = new TestErrorWatchable("four", null, "six"); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final IObservable o2 = new TestErrorWatchable("one", "two", null); + final Observable o1 = new TestErrorObservable("four", null, "six"); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o2 = new TestErrorObservable("one", "two", null); @SuppressWarnings("unchecked") - IObservable m = mergeDelayError(o1, o2); + Observable m = mergeDelayError(o1, o2); m.subscribe(stringObserver); verify(stringObserver, times(1)).onError(any(CompositeException.class)); @@ -475,12 +475,12 @@ public void testCompositeErrorDelayed1() { @Test public void testCompositeErrorDelayed2() { - final IObservable o1 = new TestErrorWatchable("four", null, "six"); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called - final IObservable o2 = new TestErrorWatchable("one", "two", null); + final Observable o1 = new TestErrorObservable("four", null, "six"); // we expect to lose "six" from the source (and it should never be sent by the source since onError was called + final Observable o2 = new TestErrorObservable("one", "two", null); @SuppressWarnings("unchecked") - IObservable m = mergeDelayError(o1, o2); - CaptureWatcher w = new CaptureWatcher(); + Observable m = mergeDelayError(o1, o2); + CaptureObserver w = new CaptureObserver(); m.subscribe(w); assertNotNull(w.e); @@ -499,19 +499,19 @@ public void testCompositeErrorDelayed2() { @Test public void testMergeObservableOfObservables() { - final IObservable o1 = new TestSynchronousWatchable(); - final IObservable o2 = new TestSynchronousWatchable(); + final Observable o1 = new TestSynchronousObservable(); + final Observable o2 = new TestSynchronousObservable(); - IObservable> observableOfObservables = new AbstractIObservable>() { + Observable> observableOfObservables = new Observable>() { @Override - public IDisposable subscribe(IObserver> observer) { + public Subscription subscribe(Observer> observer) { // simulate what would happen in an observable observer.onNext(o1); observer.onNext(o2); observer.onCompleted(); - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -522,7 +522,7 @@ public void unsubscribe() { } }; - IObservable m = mergeDelayError(observableOfObservables); + Observable m = mergeDelayError(observableOfObservables); m.subscribe(stringObserver); verify(stringObserver, never()).onError(any(Exception.class)); @@ -532,11 +532,11 @@ public void unsubscribe() { @Test public void testMergeArray() { - final IObservable o1 = new TestSynchronousWatchable(); - final IObservable o2 = new TestSynchronousWatchable(); + final Observable o1 = new TestSynchronousObservable(); + final Observable o2 = new TestSynchronousObservable(); @SuppressWarnings("unchecked") - IObservable m = mergeDelayError(o1, o2); + Observable m = mergeDelayError(o1, o2); m.subscribe(stringObserver); verify(stringObserver, never()).onError(any(Exception.class)); @@ -546,13 +546,13 @@ public void testMergeArray() { @Test public void testMergeList() { - final IObservable o1 = new TestSynchronousWatchable(); - final IObservable o2 = new TestSynchronousWatchable(); - List> listOfObservables = new ArrayList>(); + final Observable o1 = new TestSynchronousObservable(); + final Observable o2 = new TestSynchronousObservable(); + List> listOfObservables = new ArrayList>(); listOfObservables.add(o1); listOfObservables.add(o2); - IObservable m = mergeDelayError(listOfObservables); + Observable m = mergeDelayError(listOfObservables); m.subscribe(stringObserver); verify(stringObserver, never()).onError(any(Exception.class)); @@ -562,12 +562,12 @@ public void testMergeList() { @Test public void testUnSubscribe() { - TestWatchable tA = new TestWatchable(); - TestWatchable tB = new TestWatchable(); + TestObservable tA = new TestObservable(); + TestObservable tB = new TestObservable(); @SuppressWarnings("unchecked") - IObservable m = mergeDelayError(tA, tB); - IDisposable s = m.subscribe(stringObserver); + Observable m = mergeDelayError(tA, tB); + Subscription s = m.subscribe(stringObserver); tA.sendOnNext("Aone"); tB.sendOnNext("Bone"); @@ -589,11 +589,11 @@ public void testUnSubscribe() { @Test public void testMergeArrayWithThreading() { - final TestASynchronousWatchable o1 = new TestASynchronousWatchable(); - final TestASynchronousWatchable o2 = new TestASynchronousWatchable(); + final TestASynchronousObservable o1 = new TestASynchronousObservable(); + final TestASynchronousObservable o2 = new TestASynchronousObservable(); @SuppressWarnings("unchecked") - IObservable m = mergeDelayError(o1, o2); + Observable m = mergeDelayError(o1, o2); m.subscribe(stringObserver); try { @@ -608,15 +608,15 @@ public void testMergeArrayWithThreading() { verify(stringObserver, times(1)).onCompleted(); } - private static class TestSynchronousWatchable extends AbstractIObservable { + private static class TestSynchronousObservable extends Observable { @Override - public IDisposable subscribe(IObserver observer) { + public Subscription subscribe(Observer observer) { observer.onNext("hello"); observer.onCompleted(); - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -627,11 +627,11 @@ public void unsubscribe() { } } - private static class TestASynchronousWatchable extends AbstractIObservable { + private static class TestASynchronousObservable extends Observable { Thread t; @Override - public IDisposable subscribe(final IObserver observer) { + public Subscription subscribe(final Observer observer) { t = new Thread(new Runnable() { @Override @@ -643,7 +643,7 @@ public void run() { }); t.start(); - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -655,13 +655,13 @@ public void unsubscribe() { } /** - * A Watchable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. + * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. */ - private static class TestWatchable extends AbstractIObservable { + private static class TestObservable extends Observable { - IObserver observer = null; + Observer observer = null; volatile boolean unsubscribed = false; - IDisposable s = new IDisposable() { + Subscription s = new Subscription() { @Override public void unsubscribe() { @@ -671,7 +671,7 @@ public void unsubscribe() { }; - public TestWatchable() { + public TestObservable() { } /* used to simulate subscription */ @@ -691,22 +691,22 @@ public void sendOnError(Exception e) { } @Override - public IDisposable subscribe(final IObserver observer) { + public Subscription subscribe(final Observer observer) { this.observer = observer; return s; } } - private static class TestErrorWatchable extends AbstractIObservable { + private static class TestErrorObservable extends Observable { String[] valuesToReturn; - TestErrorWatchable(String... values) { + TestErrorObservable(String... values) { valuesToReturn = values; } @Override - public IDisposable subscribe(IObserver observer) { + public Subscription subscribe(Observer observer) { boolean errorThrown = false; for (String s : valuesToReturn) { if (s == null) { @@ -723,7 +723,7 @@ public IDisposable subscribe(IObserver observer) { observer.onCompleted(); } - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -734,18 +734,18 @@ public void unsubscribe() { } } - private static class TestAsyncErrorWatchable extends AbstractIObservable { + private static class TestAsyncErrorObservable extends Observable { String[] valuesToReturn; - TestAsyncErrorWatchable(String... values) { + TestAsyncErrorObservable(String... values) { valuesToReturn = values; } Thread t; @Override - public IDisposable subscribe(final IObserver observer) { + public Subscription subscribe(final Observer observer) { t = new Thread(new Runnable() { @Override @@ -771,7 +771,7 @@ public void run() { }); t.start(); - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { @@ -782,7 +782,7 @@ public void unsubscribe() { } } - private static class CaptureWatcher implements IObserver { + private static class CaptureObserver implements Observer { volatile Exception e; @Override diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorResumeNextViaFunction.java b/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorResumeNextViaFunction.java index 975281053a..82fff9ca2d 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorResumeNextViaFunction.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorResumeNextViaFunction.java @@ -8,78 +8,85 @@ import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; +import org.mockito.Mockito; import org.rx.functions.Func1; -import org.rx.reactive.AbstractIObservable; import org.rx.reactive.CompositeException; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; +public final class OperationOnErrorResumeNextViaFunction { -final class OperationOnErrorResumeNextViaFunction extends AbstractIObservable { - private final Func1, Exception> resumeFunction; - private final IObservable originalSequence; - - OperationOnErrorResumeNextViaFunction(IObservable originalSequence, Func1, Exception> resumeFunction) { - this.resumeFunction = resumeFunction; - this.originalSequence = originalSequence; + public static Observable onErrorResumeNextViaFunction(Observable originalSequence, Func1, Exception> resumeFunction) { + return new OnErrorResumeNextViaFunction(originalSequence, resumeFunction); } - public IDisposable subscribe(IObserver watcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - final IObserver observer = new AtomicWatcher(watcher, subscription); + private static class OnErrorResumeNextViaFunction extends Observable { - // AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed - final AtomicReference subscriptionRef = new AtomicReference(subscription); + private final Func1, Exception> resumeFunction; + private final Observable originalSequence; - // subscribe to the original Watchable and remember the subscription - subscription.setActual(originalSequence.subscribe(new IObserver() { - public void onNext(T value) { - // forward the successful calls - observer.onNext(value); - } + public OnErrorResumeNextViaFunction(Observable originalSequence, Func1, Exception> resumeFunction) { + this.resumeFunction = resumeFunction; + this.originalSequence = originalSequence; + } + + public Subscription subscribe(Observer Observer) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + final Observer observer = new AtomicObserver(Observer, subscription); + + // AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed + final AtomicReference subscriptionRef = new AtomicReference(subscription); - /** - * Instead of passing the onError forward, we intercept and "resume" with the resumeSequence. - */ - public void onError(Exception ex) { - /* remember what the current subscription is so we can determine if someone unsubscribes concurrently */ - AtomicWatchableSubscription currentSubscription = subscriptionRef.get(); - // check that we have not been unsubscribed before we can process the error - if (currentSubscription != null) { - try { - IObservable resumeSequence = resumeFunction.call(ex); - /* error occurred, so switch subscription to the 'resumeSequence' */ - AtomicWatchableSubscription innerSubscription = new AtomicWatchableSubscription(resumeSequence.subscribe(observer)); - /* we changed the sequence, so also change the subscription to the one of the 'resumeSequence' instead */ - if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription)) { - // we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below - // so we want to immediately unsubscribe from the resumeSequence we just subscribed to - innerSubscription.unsubscribe(); + // subscribe to the original Observable and remember the subscription + subscription.setActual(originalSequence.subscribe(new Observer() { + public void onNext(T value) { + // forward the successful calls + observer.onNext(value); + } + + /** + * Instead of passing the onError forward, we intercept and "resume" with the resumeSequence. + */ + public void onError(Exception ex) { + /* remember what the current subscription is so we can determine if someone unsubscribes concurrently */ + AtomicObservableSubscription currentSubscription = subscriptionRef.get(); + // check that we have not been unsubscribed before we can process the error + if (currentSubscription != null) { + try { + Observable resumeSequence = resumeFunction.call(ex); + /* error occurred, so switch subscription to the 'resumeSequence' */ + AtomicObservableSubscription innerSubscription = new AtomicObservableSubscription(resumeSequence.subscribe(observer)); + /* we changed the sequence, so also change the subscription to the one of the 'resumeSequence' instead */ + if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription)) { + // we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below + // so we want to immediately unsubscribe from the resumeSequence we just subscribed to + innerSubscription.unsubscribe(); + } + } catch (Exception e) { + // the resume function failed so we need to call onError + // I am using CompositeException so that both exceptions can be seen + observer.onError(new CompositeException("OnErrorResume function failed", Arrays.asList(ex, e))); } - } catch (Exception e) { - // the resume function failed so we need to call onError - // I am using CompositeException so that both exceptions can be seen - observer.onError(new CompositeException("OnErrorResume function failed", Arrays.asList(ex, e))); } } - } - public void onCompleted() { - // forward the successful calls - observer.onCompleted(); - } - })); - - return new IDisposable() { - public void unsubscribe() { - // this will get either the original, or the resumeSequence one and unsubscribe on it - IDisposable s = subscriptionRef.getAndSet(null); - if (s != null) { - s.unsubscribe(); + public void onCompleted() { + // forward the successful calls + observer.onCompleted(); } - } - }; + })); + + return new Subscription() { + public void unsubscribe() { + // this will get either the original, or the resumeSequence one and unsubscribe on it + Subscription s = subscriptionRef.getAndSet(null); + if (s != null) { + s.unsubscribe(); + } + } + }; + } } public static class UnitTest { @@ -87,22 +94,22 @@ public static class UnitTest { @Test public void testResumeNext() { final AtomicReference receivedException = new AtomicReference(); - IDisposable s = mock(IDisposable.class); - TestWatchable w = new TestWatchable(s, "one"); - Func1, Exception> resume = new Func1, Exception>() { + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one"); + Func1, Exception> resume = new Func1, Exception>() { @Override - public IObservable call(Exception t1) { + public Observable call(Exception t1) { receivedException.set(t1); - return WatchableExtensions.toWatchable("twoResume", "threeResume"); + return Observable.toObservable("twoResume", "threeResume"); } }; - IObservable watchable = new OperationOnErrorResumeNextViaFunction(w, resume); + Observable Observable = onErrorResumeNextViaFunction(w, resume); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); + Observer aObserver = mock(Observer.class); + Observable.subscribe(aObserver); try { w.t.join(); @@ -110,13 +117,13 @@ public IObservable call(Exception t1) { fail(e.getMessage()); } - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - verify(aWatcher, times(1)).onNext("one"); - verify(aWatcher, never()).onNext("two"); - verify(aWatcher, never()).onNext("three"); - verify(aWatcher, times(1)).onNext("twoResume"); - verify(aWatcher, times(1)).onNext("threeResume"); + verify(aObserver, Mockito.never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); assertNotNull(receivedException.get()); } @@ -125,21 +132,21 @@ public IObservable call(Exception t1) { */ @Test public void testFunctionThrowsError() { - IDisposable s = mock(IDisposable.class); - TestWatchable w = new TestWatchable(s, "one"); - Func1, Exception> resume = new Func1, Exception>() { + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one"); + Func1, Exception> resume = new Func1, Exception>() { @Override - public IObservable call(Exception t1) { + public Observable call(Exception t1) { throw new RuntimeException("exception from function"); } }; - IObservable watchable = new OperationOnErrorResumeNextViaFunction(w, resume); + Observable Observable = onErrorResumeNextViaFunction(w, resume); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); + Observer aObserver = mock(Observer.class); + Observable.subscribe(aObserver); try { w.t.join(); @@ -148,35 +155,35 @@ public IObservable call(Exception t1) { } // we should get the "one" value before the error - verify(aWatcher, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("one"); - // we should have received an onError call on the watcher since the resume function threw an exception - verify(aWatcher, times(1)).onError(any(Exception.class)); - verify(aWatcher, times(0)).onCompleted(); + // we should have received an onError call on the Observer since the resume function threw an exception + verify(aObserver, times(1)).onError(any(Exception.class)); + verify(aObserver, times(0)).onCompleted(); } - private static class TestWatchable extends AbstractIObservable { + private static class TestObservable extends Observable { - final IDisposable s; + final Subscription s; final String[] values; Thread t = null; - public TestWatchable(IDisposable s, String... values) { + public TestObservable(Subscription s, String... values) { this.s = s; this.values = values; } @Override - public IDisposable subscribe(final IObserver observer) { - System.out.println("TestWatchable subscribed to ..."); + public Subscription subscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); t = new Thread(new Runnable() { @Override public void run() { try { - System.out.println("running TestWatchable thread"); + System.out.println("running TestObservable thread"); for (String s : values) { - System.out.println("TestWatchable onNext: " + s); + System.out.println("TestObservable onNext: " + s); observer.onNext(s); } throw new RuntimeException("Forced Failure"); @@ -186,9 +193,9 @@ public void run() { } }); - System.out.println("starting TestWatchable thread"); + System.out.println("starting TestObservable thread"); t.start(); - System.out.println("done starting TestWatchable thread"); + System.out.println("done starting TestObservable thread"); return s; } diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorResumeNextViaObservable.java b/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorResumeNextViaObservable.java new file mode 100644 index 0000000000..9311d5381d --- /dev/null +++ b/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorResumeNextViaObservable.java @@ -0,0 +1,150 @@ +package org.rx.operations; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.Test; +import org.mockito.Mockito; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; + +public final class OperationOnErrorResumeNextViaObservable { + + public static Observable onErrorResumeNextViaObservable(Observable originalSequence, Observable resumeSequence) { + return new OnErrorResumeNextViaObservable(originalSequence, resumeSequence); + } + + private static class OnErrorResumeNextViaObservable extends Observable { + + private final Observable resumeSequence; + private final Observable originalSequence; + + public OnErrorResumeNextViaObservable(Observable originalSequence, Observable resumeSequence) { + this.resumeSequence = resumeSequence; + this.originalSequence = originalSequence; + } + + public Subscription subscribe(Observer Observer) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + final Observer observer = new AtomicObserver(Observer, subscription); + + // AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed + final AtomicReference subscriptionRef = new AtomicReference(subscription); + + // subscribe to the original Observable and remember the subscription + subscription.setActual(originalSequence.subscribe(new Observer() { + public void onNext(T value) { + // forward the successful calls + observer.onNext(value); + } + + /** + * Instead of passing the onError forward, we intercept and "resume" with the resumeSequence. + */ + public void onError(Exception ex) { + /* remember what the current subscription is so we can determine if someone unsubscribes concurrently */ + AtomicObservableSubscription currentSubscription = subscriptionRef.get(); + // check that we have not been unsubscribed before we can process the error + if (currentSubscription != null) { + /* error occurred, so switch subscription to the 'resumeSequence' */ + AtomicObservableSubscription innerSubscription = new AtomicObservableSubscription(resumeSequence.subscribe(observer)); + /* we changed the sequence, so also change the subscription to the one of the 'resumeSequence' instead */ + if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription)) { + // we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below + // so we want to immediately unsubscribe from the resumeSequence we just subscribed to + innerSubscription.unsubscribe(); + } + } + } + + public void onCompleted() { + // forward the successful calls + observer.onCompleted(); + } + })); + + return new Subscription() { + public void unsubscribe() { + // this will get either the original, or the resumeSequence one and unsubscribe on it + Subscription s = subscriptionRef.getAndSet(null); + if (s != null) { + s.unsubscribe(); + } + } + }; + } + } + + public static class UnitTest { + + @Test + public void testResumeNext() { + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one"); + Observable resume = Observable.toObservable("twoResume", "threeResume"); + Observable Observable = onErrorResumeNextViaObservable(w, resume); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Observable.subscribe(aObserver); + + try { + w.t.join(); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + + verify(aObserver, Mockito.never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, Mockito.never()).onNext("two"); + verify(aObserver, Mockito.never()).onNext("three"); + verify(aObserver, times(1)).onNext("twoResume"); + verify(aObserver, times(1)).onNext("threeResume"); + + } + + private static class TestObservable extends Observable { + + final Subscription s; + final String[] values; + Thread t = null; + + public TestObservable(Subscription s, String... values) { + this.s = s; + this.values = values; + } + + @Override + public Subscription subscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); + t = new Thread(new Runnable() { + + @Override + public void run() { + try { + System.out.println("running TestObservable thread"); + for (String s : values) { + System.out.println("TestObservable onNext: " + s); + observer.onNext(s); + } + throw new RuntimeException("Forced Failure"); + } catch (Exception e) { + observer.onError(e); + } + } + + }); + System.out.println("starting TestObservable thread"); + t.start(); + System.out.println("done starting TestObservable thread"); + return s; + } + + } + } +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorResumeNextViaWatchable.java b/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorResumeNextViaWatchable.java deleted file mode 100644 index 7bf4210549..0000000000 --- a/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorResumeNextViaWatchable.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.rx.operations; - -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - - -final class OperationOnErrorResumeNextViaWatchable extends AbstractIObservable { - private final IObservable resumeSequence; - private final IObservable originalSequence; - - OperationOnErrorResumeNextViaWatchable(IObservable originalSequence, IObservable resumeSequence) { - this.resumeSequence = resumeSequence; - this.originalSequence = originalSequence; - } - - public IDisposable subscribe(IObserver watcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - final IObserver observer = new AtomicWatcher(watcher, subscription); - - // AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed - final AtomicReference subscriptionRef = new AtomicReference(subscription); - - // subscribe to the original Watchable and remember the subscription - subscription.setActual(originalSequence.subscribe(new IObserver() { - public void onNext(T value) { - // forward the successful calls - observer.onNext(value); - } - - /** - * Instead of passing the onError forward, we intercept and "resume" with the resumeSequence. - */ - public void onError(Exception ex) { - /* remember what the current subscription is so we can determine if someone unsubscribes concurrently */ - AtomicWatchableSubscription currentSubscription = subscriptionRef.get(); - // check that we have not been unsubscribed before we can process the error - if (currentSubscription != null) { - /* error occurred, so switch subscription to the 'resumeSequence' */ - AtomicWatchableSubscription innerSubscription = new AtomicWatchableSubscription(resumeSequence.subscribe(observer)); - /* we changed the sequence, so also change the subscription to the one of the 'resumeSequence' instead */ - if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription)) { - // we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below - // so we want to immediately unsubscribe from the resumeSequence we just subscribed to - innerSubscription.unsubscribe(); - } - } - } - - public void onCompleted() { - // forward the successful calls - observer.onCompleted(); - } - })); - - return new IDisposable() { - public void unsubscribe() { - // this will get either the original, or the resumeSequence one and unsubscribe on it - IDisposable s = subscriptionRef.getAndSet(null); - if (s != null) { - s.unsubscribe(); - } - } - }; - } - - public static class UnitTest { - - @Test - public void testResumeNext() { - IDisposable s = mock(IDisposable.class); - TestWatchable w = new TestWatchable(s, "one"); - IObservable resume = WatchableExtensions.toWatchable("twoResume", "threeResume"); - IObservable watchable = new OperationOnErrorResumeNextViaWatchable(w, resume); - - @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); - - try { - w.t.join(); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - verify(aWatcher, times(1)).onNext("one"); - verify(aWatcher, never()).onNext("two"); - verify(aWatcher, never()).onNext("three"); - verify(aWatcher, times(1)).onNext("twoResume"); - verify(aWatcher, times(1)).onNext("threeResume"); - - } - - private static class TestWatchable extends AbstractIObservable { - - final IDisposable s; - final String[] values; - Thread t = null; - - public TestWatchable(IDisposable s, String... values) { - this.s = s; - this.values = values; - } - - @Override - public IDisposable subscribe(final IObserver observer) { - System.out.println("TestWatchable subscribed to ..."); - t = new Thread(new Runnable() { - - @Override - public void run() { - try { - System.out.println("running TestWatchable thread"); - for (String s : values) { - System.out.println("TestWatchable onNext: " + s); - observer.onNext(s); - } - throw new RuntimeException("Forced Failure"); - } catch (Exception e) { - observer.onError(e); - } - } - - }); - System.out.println("starting TestWatchable thread"); - t.start(); - System.out.println("done starting TestWatchable thread"); - return s; - } - - } - } -} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorReturn.java b/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorReturn.java index 1473d9f024..6ab27e6aa1 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorReturn.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationOnErrorReturn.java @@ -8,97 +8,103 @@ import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; +import org.mockito.Mockito; import org.rx.functions.Func1; -import org.rx.reactive.AbstractIObservable; import org.rx.reactive.CompositeException; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; /** * When an onError occurs the resumeFunction will be executed and it's response passed to onNext instead of calling onError. */ -final class OperationOnErrorReturn extends AbstractIObservable { - private final Func1 resumeFunction; - private final IObservable originalSequence; +public final class OperationOnErrorReturn { - OperationOnErrorReturn(IObservable originalSequence, Func1 resumeFunction) { - this.resumeFunction = resumeFunction; - this.originalSequence = originalSequence; + public static Observable onErrorReturn(Observable originalSequence, Func1 resumeFunction) { + return new OnErrorReturn(originalSequence, resumeFunction); } - public IDisposable subscribe(IObserver watcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - final IObserver observer = new AtomicWatcher(watcher, subscription); + private static class OnErrorReturn extends Observable { + private final Func1 resumeFunction; + private final Observable originalSequence; + + public OnErrorReturn(Observable originalSequence, Func1 resumeFunction) { + this.resumeFunction = resumeFunction; + this.originalSequence = originalSequence; + } - // AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed - final AtomicReference subscriptionRef = new AtomicReference(subscription); + public Subscription subscribe(Observer Observer) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + final Observer observer = new AtomicObserver(Observer, subscription); - // subscribe to the original Watchable and remember the subscription - subscription.setActual(originalSequence.subscribe(new IObserver() { - public void onNext(T value) { - // forward the successful calls - observer.onNext(value); - } + // AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed + final AtomicReference subscriptionRef = new AtomicReference(subscription); - /** - * Instead of passing the onError forward, we intercept and "resume" with the resumeSequence. - */ - public void onError(Exception ex) { - /* remember what the current subscription is so we can determine if someone unsubscribes concurrently */ - AtomicWatchableSubscription currentSubscription = subscriptionRef.get(); - // check that we have not been unsubscribed before we can process the error - if (currentSubscription != null) { - try { - /* error occurred, so execute the function, give it the exception and call onNext with the response */ - onNext(resumeFunction.call(ex)); - /* - * we are not handling an exception thrown from this function ... should we do something? - * error handling within an error handler is a weird one to determine what we should do - * right now I'm going to just let it throw whatever exceptions occur (such as NPE) - * but I'm considering calling the original watcher.onError to act as if this OnErrorReturn operator didn't happen - */ - - /* we are now completed */ - onCompleted(); - - /* unsubscribe since it blew up */ - currentSubscription.unsubscribe(); - } catch (Exception e) { - // the return function failed so we need to call onError - // I am using CompositeException so that both exceptions can be seen - observer.onError(new CompositeException("OnErrorReturn function failed", Arrays.asList(ex, e))); + // subscribe to the original Observable and remember the subscription + subscription.setActual(originalSequence.subscribe(new Observer() { + public void onNext(T value) { + // forward the successful calls + observer.onNext(value); + } + + /** + * Instead of passing the onError forward, we intercept and "resume" with the resumeSequence. + */ + public void onError(Exception ex) { + /* remember what the current subscription is so we can determine if someone unsubscribes concurrently */ + AtomicObservableSubscription currentSubscription = subscriptionRef.get(); + // check that we have not been unsubscribed before we can process the error + if (currentSubscription != null) { + try { + /* error occurred, so execute the function, give it the exception and call onNext with the response */ + onNext(resumeFunction.call(ex)); + /* + * we are not handling an exception thrown from this function ... should we do something? + * error handling within an error handler is a weird one to determine what we should do + * right now I'm going to just let it throw whatever exceptions occur (such as NPE) + * but I'm considering calling the original Observer.onError to act as if this OnErrorReturn operator didn't happen + */ + + /* we are now completed */ + onCompleted(); + + /* unsubscribe since it blew up */ + currentSubscription.unsubscribe(); + } catch (Exception e) { + // the return function failed so we need to call onError + // I am using CompositeException so that both exceptions can be seen + observer.onError(new CompositeException("OnErrorReturn function failed", Arrays.asList(ex, e))); + } } } - } - public void onCompleted() { - // forward the successful calls - observer.onCompleted(); - } - })); - - return new IDisposable() { - public void unsubscribe() { - // this will get either the original, or the resumeSequence one and unsubscribe on it - IDisposable s = subscriptionRef.getAndSet(null); - if (s != null) { - s.unsubscribe(); + public void onCompleted() { + // forward the successful calls + observer.onCompleted(); } - } - }; + })); + + return new Subscription() { + public void unsubscribe() { + // this will get either the original, or the resumeSequence one and unsubscribe on it + Subscription s = subscriptionRef.getAndSet(null); + if (s != null) { + s.unsubscribe(); + } + } + }; + } } public static class UnitTest { @Test public void testResumeNext() { - IDisposable s = mock(IDisposable.class); - TestWatchable w = new TestWatchable(s, "one"); + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one"); final AtomicReference capturedException = new AtomicReference(); - IObservable watchable = new OperationOnErrorReturn(w, new Func1() { + Observable Observable = onErrorReturn(w, new Func1() { @Override public String call(Exception e) { @@ -109,8 +115,8 @@ public String call(Exception e) { }); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); + Observer aObserver = mock(Observer.class); + Observable.subscribe(aObserver); try { w.t.join(); @@ -118,10 +124,10 @@ public String call(Exception e) { fail(e.getMessage()); } - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - verify(aWatcher, times(1)).onNext("one"); - verify(aWatcher, times(1)).onNext("failure"); + verify(aObserver, Mockito.never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("failure"); assertNotNull(capturedException.get()); } @@ -130,11 +136,11 @@ public String call(Exception e) { */ @Test public void testFunctionThrowsError() { - IDisposable s = mock(IDisposable.class); - TestWatchable w = new TestWatchable(s, "one"); + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one"); final AtomicReference capturedException = new AtomicReference(); - IObservable watchable = new OperationOnErrorReturn(w, new Func1() { + Observable Observable = onErrorReturn(w, new Func1() { @Override public String call(Exception e) { @@ -145,8 +151,8 @@ public String call(Exception e) { }); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); + Observer aObserver = mock(Observer.class); + Observable.subscribe(aObserver); try { w.t.join(); @@ -155,36 +161,36 @@ public String call(Exception e) { } // we should get the "one" value before the error - verify(aWatcher, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("one"); - // we should have received an onError call on the watcher since the resume function threw an exception - verify(aWatcher, times(1)).onError(any(Exception.class)); - verify(aWatcher, times(0)).onCompleted(); + // we should have received an onError call on the Observer since the resume function threw an exception + verify(aObserver, times(1)).onError(any(Exception.class)); + verify(aObserver, times(0)).onCompleted(); assertNotNull(capturedException.get()); } - private static class TestWatchable extends AbstractIObservable { + private static class TestObservable extends Observable { - final IDisposable s; + final Subscription s; final String[] values; Thread t = null; - public TestWatchable(IDisposable s, String... values) { + public TestObservable(Subscription s, String... values) { this.s = s; this.values = values; } @Override - public IDisposable subscribe(final IObserver observer) { - System.out.println("TestWatchable subscribed to ..."); + public Subscription subscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); t = new Thread(new Runnable() { @Override public void run() { try { - System.out.println("running TestWatchable thread"); + System.out.println("running TestObservable thread"); for (String s : values) { - System.out.println("TestWatchable onNext: " + s); + System.out.println("TestObservable onNext: " + s); observer.onNext(s); } throw new RuntimeException("Forced Failure"); @@ -194,9 +200,9 @@ public void run() { } }); - System.out.println("starting TestWatchable thread"); + System.out.println("starting TestObservable thread"); t.start(); - System.out.println("done starting TestWatchable thread"); + System.out.println("done starting TestObservable thread"); return s; } diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationScan.java b/rxjava-core/src/main/java/org/rx/operations/OperationScan.java index fca92bddbb..2fc34b6dc3 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationScan.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationScan.java @@ -7,13 +7,11 @@ import org.junit.Test; import org.mockito.MockitoAnnotations; import org.rx.functions.Func2; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; - -/* package */class OperationScan { +public final class OperationScan { /** * Applies an accumulator function over an observable sequence and returns each intermediate result with the specified source and accumulator. * @@ -24,10 +22,10 @@ * @param accumulator * An accumulator function to be invoked on each element from the sequence. * - * @return An observable sequence whose elements are the result of accumulating the output from the list of IObservables. + * @return An observable sequence whose elements are the result of accumulating the output from the list of Observables. * @see http://msdn.microsoft.com/en-us/library/hh211665(v=vs.103).aspx */ - public static IObservable scan(IObservable sequence, T initialValue, Func2 accumulator) { + public static Observable scan(Observable sequence, T initialValue, Func2 accumulator) { return new Accumulator(sequence, initialValue, accumulator); } @@ -39,30 +37,30 @@ public static IObservable scan(IObservable sequence, T initialValue, F * @param accumulator * An accumulator function to be invoked on each element from the sequence. * - * @return An observable sequence whose elements are the result of accumulating the output from the list of IObservables. + * @return An observable sequence whose elements are the result of accumulating the output from the list of Observables. * @see http://msdn.microsoft.com/en-us/library/hh211665(v=vs.103).aspx */ - public static IObservable scan(IObservable sequence, Func2 accumulator) { + public static Observable scan(Observable sequence, Func2 accumulator) { return new Accumulator(sequence, null, accumulator); } - private static class Accumulator extends AbstractIObservable { - private final IObservable sequence; + private static class Accumulator extends Observable { + private final Observable sequence; private final T initialValue; private Func2 accumlatorFunction; - private Accumulator(IObservable sequence, T initialValue, Func2 accumulator) { + private Accumulator(Observable sequence, T initialValue, Func2 accumulator) { this.sequence = sequence; this.initialValue = initialValue; this.accumlatorFunction = accumulator; } - public IDisposable subscribe(final IObserver observer) { + public Subscription subscribe(final Observer observer) { - final AtomicWatchableSubscription s = new AtomicWatchableSubscription(); - final AtomicWatcher watcher = new AtomicWatcher(observer, s); + final AtomicObservableSubscription s = new AtomicObservableSubscription(); + final AtomicObserver Observer = new AtomicObserver(observer, s); - s.setActual(sequence.subscribe(new IObserver() { + s.setActual(sequence.subscribe(new Observer() { private T acc = initialValue; private boolean hasSentInitialValue = false; @@ -83,7 +81,7 @@ public synchronized void onNext(T value) { } if (!hasSentInitialValue) { hasSentInitialValue = true; - watcher.onNext(acc); + Observer.onNext(acc); } try { @@ -93,25 +91,25 @@ public synchronized void onNext(T value) { onError(new IllegalArgumentException("Null is an unsupported return value for an accumulator.")); return; } - watcher.onNext(acc); + Observer.onNext(acc); } catch (Exception ex) { - watcher.onError(ex); + Observer.onError(ex); // unsubscribe since we blew up s.unsubscribe(); } } public void onError(Exception ex) { - watcher.onError(ex); + Observer.onError(ex); } // synchronized because we access 'hasSentInitialValue' public synchronized void onCompleted() { // if only one sequence value existed, we send it without any accumulation if (!hasSentInitialValue) { - watcher.onNext(acc); + Observer.onNext(acc); } - watcher.onCompleted(); + Observer.onCompleted(); } })); @@ -129,11 +127,11 @@ public void before() { @Test public void testScanIntegersWithInitialValue() { @SuppressWarnings("unchecked") - IObserver watcher = mock(IObserver.class); + Observer Observer = mock(Observer.class); - IObservable observable = WatchableExtensions.toWatchable(1, 2, 3); + Observable observable = Observable.toObservable(1, 2, 3); - IObservable m = scan(observable, 0, new Func2() { + Observable m = scan(observable, 0, new Func2() { @Override public Integer call(Integer t1, Integer t2) { @@ -141,26 +139,26 @@ public Integer call(Integer t1, Integer t2) { } }); - m.subscribe(watcher); - - verify(watcher, never()).onError(any(Exception.class)); - verify(watcher, times(1)).onNext(0); - verify(watcher, times(1)).onNext(1); - verify(watcher, times(1)).onNext(3); - verify(watcher, times(1)).onNext(6); - verify(watcher, times(4)).onNext(anyInt()); - verify(watcher, times(1)).onCompleted(); - verify(watcher, never()).onError(any(Exception.class)); + m.subscribe(Observer); + + verify(Observer, never()).onError(any(Exception.class)); + verify(Observer, times(1)).onNext(0); + verify(Observer, times(1)).onNext(1); + verify(Observer, times(1)).onNext(3); + verify(Observer, times(1)).onNext(6); + verify(Observer, times(4)).onNext(anyInt()); + verify(Observer, times(1)).onCompleted(); + verify(Observer, never()).onError(any(Exception.class)); } @Test public void testScanIntegersWithoutInitialValue() { @SuppressWarnings("unchecked") - IObserver watcher = mock(IObserver.class); + Observer Observer = mock(Observer.class); - IObservable observable = WatchableExtensions.toWatchable(1, 2, 3); + Observable observable = Observable.toObservable(1, 2, 3); - IObservable m = scan(observable, new Func2() { + Observable m = scan(observable, new Func2() { @Override public Integer call(Integer t1, Integer t2) { @@ -168,26 +166,26 @@ public Integer call(Integer t1, Integer t2) { } }); - m.subscribe(watcher); - - verify(watcher, never()).onError(any(Exception.class)); - verify(watcher, never()).onNext(0); - verify(watcher, times(1)).onNext(1); - verify(watcher, times(1)).onNext(3); - verify(watcher, times(1)).onNext(6); - verify(watcher, times(3)).onNext(anyInt()); - verify(watcher, times(1)).onCompleted(); - verify(watcher, never()).onError(any(Exception.class)); + m.subscribe(Observer); + + verify(Observer, never()).onError(any(Exception.class)); + verify(Observer, never()).onNext(0); + verify(Observer, times(1)).onNext(1); + verify(Observer, times(1)).onNext(3); + verify(Observer, times(1)).onNext(6); + verify(Observer, times(3)).onNext(anyInt()); + verify(Observer, times(1)).onCompleted(); + verify(Observer, never()).onError(any(Exception.class)); } @Test public void testScanIntegersWithoutInitialValueAndOnlyOneValue() { @SuppressWarnings("unchecked") - IObserver watcher = mock(IObserver.class); + Observer Observer = mock(Observer.class); - IObservable observable = WatchableExtensions.toWatchable(1); + Observable observable = Observable.toObservable(1); - IObservable m = scan(observable, new Func2() { + Observable m = scan(observable, new Func2() { @Override public Integer call(Integer t1, Integer t2) { @@ -195,14 +193,14 @@ public Integer call(Integer t1, Integer t2) { } }); - m.subscribe(watcher); - - verify(watcher, never()).onError(any(Exception.class)); - verify(watcher, never()).onNext(0); - verify(watcher, times(1)).onNext(1); - verify(watcher, times(1)).onNext(anyInt()); - verify(watcher, times(1)).onCompleted(); - verify(watcher, never()).onError(any(Exception.class)); + m.subscribe(Observer); + + verify(Observer, never()).onError(any(Exception.class)); + verify(Observer, never()).onNext(0); + verify(Observer, times(1)).onNext(1); + verify(Observer, times(1)).onNext(anyInt()); + verify(Observer, times(1)).onCompleted(); + verify(Observer, never()).onError(any(Exception.class)); } } diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationSkip.java b/rxjava-core/src/main/java/org/rx/operations/OperationSkip.java index 62986b9b88..45394989fa 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationSkip.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationSkip.java @@ -6,21 +6,19 @@ import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; /** - * Skips a specified number of contiguous values from the start of a watchable sequence and then returns the remaining values. + * Skips a specified number of contiguous values from the start of a Observable sequence and then returns the remaining values. * * @param */ -/* package */final class OperationSkip { +public final class OperationSkip { /** - * Skips a specified number of contiguous values from the start of a watchable sequence and then returns the remaining values. + * Skips a specified number of contiguous values from the start of a Observable sequence and then returns the remaining values. * * @param items * @param num @@ -28,16 +26,16 @@ * * @see http://msdn.microsoft.com/en-us/library/hh229847(v=vs.103).aspx */ - public static IObservable skip(final IObservable items, final int num) { - // wrap in a Watchable so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take rather than 1 handing both, which is not thread-safe. - return new AbstractIObservable() { + public static Observable skip(final Observable items, final int num) { + // wrap in a Observable so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take rather than 1 handing both, which is not thread-safe. + return new Observable() { @Override - public IDisposable subscribe(IObserver actualWatcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - // wrap in AtomicWatcher so that onNext calls are not interleaved but received + public Subscription subscribe(Observer actualObserver) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + // wrap in AtomicObserver so that onNext calls are not interleaved but received // in the order they are called - subscription.setActual(new Skip(items, num).subscribe(new AtomicWatcher(actualWatcher, subscription))); + subscription.setActual(new Skip(items, num).subscribe(new AtomicObserver(actualObserver, subscription))); return subscription; } @@ -51,48 +49,48 @@ public IDisposable subscribe(IObserver actualWatcher) { * * @param */ - private static class Skip extends AbstractIObservable { + private static class Skip extends Observable { private final int num; - private final IObservable items; - private AtomicWatcher atomicWatcher; - private AtomicWatchableSubscription subscription = new AtomicWatchableSubscription();; + private final Observable items; + private AtomicObserver atomicObserver; + private AtomicObservableSubscription subscription = new AtomicObservableSubscription();; - Skip(final IObservable items, final int num) { + Skip(final Observable items, final int num) { this.num = num; this.items = items; } - public IDisposable subscribe(IObserver actualWatcher) { - atomicWatcher = new AtomicWatcher(actualWatcher, subscription); - subscription.setActual(items.subscribe(new ItemWatcher())); + public Subscription subscribe(Observer actualObserver) { + atomicObserver = new AtomicObserver(actualObserver, subscription); + subscription.setActual(items.subscribe(new ItemObserver())); return subscription; } /** - * Used to subscribe to the 'items' Watchable sequence and forward to the actualWatcher up to 'num' count. + * Used to subscribe to the 'items' Observable sequence and forward to the actualObserver up to 'num' count. */ - private class ItemWatcher implements IObserver { + private class ItemObserver implements Observer { private AtomicInteger counter = new AtomicInteger(); - public ItemWatcher() { + public ItemObserver() { } @Override public void onCompleted() { - atomicWatcher.onCompleted(); + atomicObserver.onCompleted(); } @Override public void onError(Exception e) { - atomicWatcher.onError(e); + atomicObserver.onError(e); } @Override public void onNext(T args) { // skip them until we reach the 'num' value if (counter.incrementAndGet() > num) { - atomicWatcher.onNext(args); + atomicObserver.onNext(args); } } @@ -104,32 +102,32 @@ public static class UnitTest { @Test public void testSkip1() { - IObservable w = WatchableExtensions.toWatchable("one", "two", "three"); - IObservable skip = skip(w, 2); + Observable w = Observable.toObservable("one", "two", "three"); + Observable skip = skip(w, 2); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - skip.subscribe(aWatcher); - verify(aWatcher, never()).onNext("one"); - verify(aWatcher, never()).onNext("two"); - verify(aWatcher, times(1)).onNext("three"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + Observer aObserver = mock(Observer.class); + skip.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); } @Test public void testSkip2() { - IObservable w = WatchableExtensions.toWatchable("one", "two", "three"); - IObservable skip = skip(w, 1); + Observable w = Observable.toObservable("one", "two", "three"); + Observable skip = skip(w, 1); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - skip.subscribe(aWatcher); - verify(aWatcher, never()).onNext("one"); - verify(aWatcher, times(1)).onNext("two"); - verify(aWatcher, times(1)).onNext("three"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + Observer aObserver = mock(Observer.class); + skip.subscribe(aObserver); + verify(aObserver, never()).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); } } diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationSynchronize.java b/rxjava-core/src/main/java/org/rx/operations/OperationSynchronize.java index cb8b5041b7..a96d02ddbc 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationSynchronize.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationSynchronize.java @@ -4,11 +4,10 @@ import static org.mockito.Mockito.*; import org.junit.Test; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - +import org.mockito.Mockito; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; /** * An observable that wraps an observable of the same type and then enforces the semantics @@ -21,7 +20,7 @@ * @param * The type of the observable sequence. */ -/* package */class OperationSynchronize extends AbstractIObservable { +public final class OperationSynchronize { /** * Accepts an observable and wraps it in another observable which ensures that the resulting observable is well-behaved. @@ -34,22 +33,26 @@ * @param * @return */ - public static IObservable synchronize(IObservable observable) { - return new OperationSynchronize(observable); + public static Observable synchronize(Observable observable) { + return new Synchronize(observable); } - public OperationSynchronize(IObservable innerObservable) { - this.innerObservable = innerObservable; - } + private static class Synchronize extends Observable { + + public Synchronize(Observable innerObservable) { + this.innerObservable = innerObservable; + } + + private Observable innerObservable; + private AtomicObserverSingleThreaded atomicObserver; - private IObservable innerObservable; - private AtomicWatcher atomicWatcher; + public Subscription subscribe(Observer observer) { + AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + atomicObserver = new AtomicObserverSingleThreaded(observer, subscription); + subscription.setActual(innerObservable.subscribe(atomicObserver)); + return subscription; + } - public IDisposable subscribe(IObserver watcher) { - AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - atomicWatcher = new AtomicWatcher(watcher, subscription); - subscription.setActual(innerObservable.subscribe(atomicWatcher)); - return subscription; } public static class UnitTest { @@ -59,19 +62,19 @@ public static class UnitTest { */ @Test public void testOnCompletedAfterUnSubscribe() { - TestWatchable t = new TestWatchable(null); - IObservable st = synchronize(t); + TestObservable t = new TestObservable(null); + Observable st = synchronize(t); @SuppressWarnings("unchecked") - IObserver w = mock(IObserver.class); - IDisposable ws = st.subscribe(w); + Observer w = mock(Observer.class); + Subscription ws = st.subscribe(w); t.sendOnNext("one"); ws.unsubscribe(); t.sendOnCompleted(); verify(w, times(1)).onNext("one"); - verify(w, never()).onCompleted(); + verify(w, Mockito.never()).onCompleted(); } /** @@ -79,19 +82,19 @@ public void testOnCompletedAfterUnSubscribe() { */ @Test public void testOnNextAfterUnSubscribe() { - TestWatchable t = new TestWatchable(null); - IObservable st = synchronize(t); + TestObservable t = new TestObservable(null); + Observable st = synchronize(t); @SuppressWarnings("unchecked") - IObserver w = mock(IObserver.class); - IDisposable ws = st.subscribe(w); + Observer w = mock(Observer.class); + Subscription ws = st.subscribe(w); t.sendOnNext("one"); ws.unsubscribe(); t.sendOnNext("two"); verify(w, times(1)).onNext("one"); - verify(w, never()).onNext("two"); + verify(w, Mockito.never()).onNext("two"); } /** @@ -99,19 +102,19 @@ public void testOnNextAfterUnSubscribe() { */ @Test public void testOnErrorAfterUnSubscribe() { - TestWatchable t = new TestWatchable(null); - IObservable st = synchronize(t); + TestObservable t = new TestObservable(null); + Observable st = synchronize(t); @SuppressWarnings("unchecked") - IObserver w = mock(IObserver.class); - IDisposable ws = st.subscribe(w); + Observer w = mock(Observer.class); + Subscription ws = st.subscribe(w); t.sendOnNext("one"); ws.unsubscribe(); t.sendOnError(new RuntimeException("bad")); verify(w, times(1)).onNext("one"); - verify(w, never()).onError(any(Exception.class)); + verify(w, Mockito.never()).onError(any(Exception.class)); } /** @@ -119,13 +122,13 @@ public void testOnErrorAfterUnSubscribe() { */ @Test public void testOnNextAfterOnError() { - TestWatchable t = new TestWatchable(null); - IObservable st = synchronize(t); + TestObservable t = new TestObservable(null); + Observable st = synchronize(t); @SuppressWarnings("unchecked") - IObserver w = mock(IObserver.class); + Observer w = mock(Observer.class); @SuppressWarnings("unused") - IDisposable ws = st.subscribe(w); + Subscription ws = st.subscribe(w); t.sendOnNext("one"); t.sendOnError(new RuntimeException("bad")); @@ -133,7 +136,7 @@ public void testOnNextAfterOnError() { verify(w, times(1)).onNext("one"); verify(w, times(1)).onError(any(Exception.class)); - verify(w, never()).onNext("two"); + verify(w, Mockito.never()).onNext("two"); } /** @@ -141,13 +144,13 @@ public void testOnNextAfterOnError() { */ @Test public void testOnCompletedAfterOnError() { - TestWatchable t = new TestWatchable(null); - IObservable st = synchronize(t); + TestObservable t = new TestObservable(null); + Observable st = synchronize(t); @SuppressWarnings("unchecked") - IObserver w = mock(IObserver.class); + Observer w = mock(Observer.class); @SuppressWarnings("unused") - IDisposable ws = st.subscribe(w); + Subscription ws = st.subscribe(w); t.sendOnNext("one"); t.sendOnError(new RuntimeException("bad")); @@ -155,7 +158,7 @@ public void testOnCompletedAfterOnError() { verify(w, times(1)).onNext("one"); verify(w, times(1)).onError(any(Exception.class)); - verify(w, never()).onCompleted(); + verify(w, Mockito.never()).onCompleted(); } /** @@ -163,22 +166,22 @@ public void testOnCompletedAfterOnError() { */ @Test public void testOnNextAfterOnCompleted() { - TestWatchable t = new TestWatchable(null); - IObservable st = synchronize(t); + TestObservable t = new TestObservable(null); + Observable st = synchronize(t); @SuppressWarnings("unchecked") - IObserver w = mock(IObserver.class); + Observer w = mock(Observer.class); @SuppressWarnings("unused") - IDisposable ws = st.subscribe(w); + Subscription ws = st.subscribe(w); t.sendOnNext("one"); t.sendOnCompleted(); t.sendOnNext("two"); verify(w, times(1)).onNext("one"); - verify(w, never()).onNext("two"); + verify(w, Mockito.never()).onNext("two"); verify(w, times(1)).onCompleted(); - verify(w, never()).onError(any(Exception.class)); + verify(w, Mockito.never()).onError(any(Exception.class)); } /** @@ -186,13 +189,13 @@ public void testOnNextAfterOnCompleted() { */ @Test public void testOnErrorAfterOnCompleted() { - TestWatchable t = new TestWatchable(null); - IObservable st = synchronize(t); + TestObservable t = new TestObservable(null); + Observable st = synchronize(t); @SuppressWarnings("unchecked") - IObserver w = mock(IObserver.class); + Observer w = mock(Observer.class); @SuppressWarnings("unused") - IDisposable ws = st.subscribe(w); + Subscription ws = st.subscribe(w); t.sendOnNext("one"); t.sendOnCompleted(); @@ -200,17 +203,17 @@ public void testOnErrorAfterOnCompleted() { verify(w, times(1)).onNext("one"); verify(w, times(1)).onCompleted(); - verify(w, never()).onError(any(Exception.class)); + verify(w, Mockito.never()).onError(any(Exception.class)); } /** - * A Watchable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. + * A Observable that doesn't do the right thing on UnSubscribe/Error/etc in that it will keep sending events down the pipe regardless of what happens. */ - private static class TestWatchable extends AbstractIObservable { + private static class TestObservable extends Observable { - IObserver observer = null; + Observer observer = null; - public TestWatchable(IDisposable s) { + public TestObservable(Subscription s) { } /* used to simulate subscription */ @@ -229,13 +232,13 @@ public void sendOnError(Exception e) { } @Override - public IDisposable subscribe(final IObserver observer) { + public Subscription subscribe(final Observer observer) { this.observer = observer; - return new IDisposable() { + return new Subscription() { @Override public void unsubscribe() { - // going to do nothing to pretend I'm a bad Watchable that keeps allowing events to be sent + // going to do nothing to pretend I'm a bad Observable that keeps allowing events to be sent } }; diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationTake.java b/rxjava-core/src/main/java/org/rx/operations/OperationTake.java index 365c122bbe..13aed147ad 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationTake.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationTake.java @@ -7,18 +7,16 @@ import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; /** * Returns a specified number of contiguous values from the start of an observable sequence. * * @param */ -/* package */final class OperationTake { +public final class OperationTake { /** * Returns a specified number of contiguous values from the start of an observable sequence. @@ -27,16 +25,16 @@ * @param num * @return */ - public static IObservable take(final IObservable items, final int num) { + public static Observable take(final Observable items, final int num) { // wrap in a Watchbable so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take rather than 1 handing both, which is not thread-safe. - return new AbstractIObservable() { + return new Observable() { @Override - public IDisposable subscribe(IObserver actualWatcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - // wrap in AtomicWatcherSingleThreaded so that onNext calls are not interleaved but received + public Subscription subscribe(Observer actualObserver) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + // wrap in AtomicObserverSingleThreaded so that onNext calls are not interleaved but received // in the order they are called - subscription.setActual(new Take(items, num).subscribe(new AtomicWatcher(actualWatcher, subscription))); + subscription.setActual(new Take(items, num).subscribe(new AtomicObserver(actualObserver, subscription))); return subscription; } @@ -50,54 +48,54 @@ public IDisposable subscribe(IObserver actualWatcher) { *

          * This should all be fine as long as it's kept as a private class and a new instance created from static factory method above. *

          - * Note how the take() factory method above protects us from a single instance being exposed with the Watchable wrapper handling the subscribe flow. + * Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow. * * @param */ - private static class Take extends AbstractIObservable { + private static class Take extends Observable { private final int num; - private final IObservable items; - private AtomicWatcher atomicWatcher; - private AtomicWatchableSubscription subscription = new AtomicWatchableSubscription();; + private final Observable items; + private AtomicObserver atomicObserver; + private AtomicObservableSubscription subscription = new AtomicObservableSubscription();; - Take(final IObservable items, final int num) { + Take(final Observable items, final int num) { this.num = num; this.items = items; } - public IDisposable subscribe(IObserver actualWatcher) { - atomicWatcher = new AtomicWatcher(actualWatcher, subscription); - subscription.setActual(items.subscribe(new ItemWatcher())); + public Subscription subscribe(Observer actualObserver) { + atomicObserver = new AtomicObserver(actualObserver, subscription); + subscription.setActual(items.subscribe(new ItemObserver())); return subscription; } /** - * Used to subscribe to the 'items' Watchable sequence and forward to the actualWatcher up to 'num' count. + * Used to subscribe to the 'items' Observable sequence and forward to the actualObserver up to 'num' count. */ - private class ItemWatcher implements IObserver { + private class ItemObserver implements Observer { private AtomicInteger counter = new AtomicInteger(); - public ItemWatcher() { + public ItemObserver() { } @Override public void onCompleted() { - atomicWatcher.onCompleted(); + atomicObserver.onCompleted(); } @Override public void onError(Exception e) { - atomicWatcher.onError(e); + atomicObserver.onError(e); } @Override public void onNext(T args) { if (counter.getAndIncrement() < num) { - atomicWatcher.onNext(args); + atomicObserver.onNext(args); } else { // we are done so let's unsubscribe - atomicWatcher.onCompleted(); + atomicObserver.onCompleted(); subscription.unsubscribe(); } } @@ -110,45 +108,45 @@ public static class UnitTest { @Test public void testTake1() { - IObservable w = WatchableExtensions.toWatchable("one", "two", "three"); - IObservable take = take(w, 2); + Observable w = Observable.toObservable("one", "two", "three"); + Observable take = take(w, 2); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - take.subscribe(aWatcher); - verify(aWatcher, times(1)).onNext("one"); - verify(aWatcher, times(1)).onNext("two"); - verify(aWatcher, never()).onNext("three"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); } @Test public void testTake2() { - IObservable w = WatchableExtensions.toWatchable("one", "two", "three"); - IObservable take = take(w, 1); + Observable w = Observable.toObservable("one", "two", "three"); + Observable take = take(w, 1); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - take.subscribe(aWatcher); - verify(aWatcher, times(1)).onNext("one"); - verify(aWatcher, never()).onNext("two"); - verify(aWatcher, never()).onNext("three"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + Observer aObserver = mock(Observer.class); + take.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); } @Test public void testUnsubscribeAfterTake() { - IDisposable s = mock(IDisposable.class); - TestWatchable w = new TestWatchable(s, "one", "two", "three"); + Subscription s = mock(Subscription.class); + TestObservable w = new TestObservable(s, "one", "two", "three"); @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - IObservable take = take(w, 1); - take.subscribe(aWatcher); + Observer aObserver = mock(Observer.class); + Observable take = take(w, 1); + take.subscribe(aObserver); - // wait for the watchable to complete + // wait for the Observable to complete try { w.t.join(); } catch (Exception e) { @@ -156,35 +154,35 @@ public void testUnsubscribeAfterTake() { fail(e.getMessage()); } - System.out.println("TestWatchable thread finished"); - verify(aWatcher, times(1)).onNext("one"); - verify(aWatcher, never()).onNext("two"); - verify(aWatcher, never()).onNext("three"); + System.out.println("TestObservable thread finished"); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, never()).onNext("two"); + verify(aObserver, never()).onNext("three"); verify(s, times(1)).unsubscribe(); } - private static class TestWatchable extends AbstractIObservable { + private static class TestObservable extends Observable { - final IDisposable s; + final Subscription s; final String[] values; Thread t = null; - public TestWatchable(IDisposable s, String... values) { + public TestObservable(Subscription s, String... values) { this.s = s; this.values = values; } @Override - public IDisposable subscribe(final IObserver observer) { - System.out.println("TestWatchable subscribed to ..."); + public Subscription subscribe(final Observer observer) { + System.out.println("TestObservable subscribed to ..."); t = new Thread(new Runnable() { @Override public void run() { try { - System.out.println("running TestWatchable thread"); + System.out.println("running TestObservable thread"); for (String s : values) { - System.out.println("TestWatchable onNext: " + s); + System.out.println("TestObservable onNext: " + s); observer.onNext(s); } observer.onCompleted(); @@ -194,9 +192,9 @@ public void run() { } }); - System.out.println("starting TestWatchable thread"); + System.out.println("starting TestObservable thread"); t.start(); - System.out.println("done starting TestWatchable thread"); + System.out.println("done starting TestObservable thread"); return s; } diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationToObservableFunction.java b/rxjava-core/src/main/java/org/rx/operations/OperationToObservableFunction.java new file mode 100644 index 0000000000..cfa2501caa --- /dev/null +++ b/rxjava-core/src/main/java/org/rx/operations/OperationToObservableFunction.java @@ -0,0 +1,77 @@ +package org.rx.operations; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import org.junit.Test; +import org.mockito.Mockito; +import org.rx.functions.Func1; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; + +/** + * Accepts a Function and makes it into a Observable. + *

          + * This is equivalent to Rx Observable.Create + * + * @see http://msdn.microsoft.com/en-us/library/hh229114(v=vs.103).aspx + * @see Observable.toObservable + * @see Observable.create + */ +public final class OperationToObservableFunction { + + public static Observable toObservableFunction(Func1> func) { + return new ToObservableFunction(func); + } + + private static class ToObservableFunction extends Observable { + private final Func1> func; + + public ToObservableFunction(Func1> func) { + this.func = func; + } + + @Override + public Subscription subscribe(Observer Observer) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + // We specifically use the SingleThreaded AtomicObserver since we can't ensure the implementation is thread-safe + // so will not allow it to use the MultiThreaded version even when other operators are doing so + final Observer atomicObserver = new AtomicObserverSingleThreaded(Observer, subscription); + // if func.call is synchronous, then the subscription won't matter as it can't ever be called + // if func.call is asynchronous, then the subscription will get set and can be unsubscribed from + subscription.setActual(func.call(atomicObserver)); + + return subscription; + } + } + + public static class UnitTest { + + @Test + public void testCreate() { + + Observable observable = toObservableFunction(new Func1>() { + + @Override + public Subscription call(Observer Observer) { + Observer.onNext("one"); + Observer.onNext("two"); + Observer.onNext("three"); + Observer.onCompleted(); + return Observable.noOpSubscription(); + } + + }); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + } + } +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationToObservableIterable.java b/rxjava-core/src/main/java/org/rx/operations/OperationToObservableIterable.java new file mode 100644 index 0000000000..a462b5ee95 --- /dev/null +++ b/rxjava-core/src/main/java/org/rx/operations/OperationToObservableIterable.java @@ -0,0 +1,62 @@ +package org.rx.operations; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.Arrays; + +import org.junit.Test; +import org.mockito.Mockito; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; + +/** + * Accepts an Iterable object and exposes it as an Observable. + * + * @param + * The type of the Iterable sequence. + */ +public final class OperationToObservableIterable { + + public static Observable toObservableIterable(Iterable list) { + return new ToObservableIterable(list); + } + + private static class ToObservableIterable extends Observable { + public ToObservableIterable(Iterable list) { + this.iterable = list; + } + + public Iterable iterable; + + public Subscription subscribe(Observer Observer) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(Observable.noOpSubscription()); + final Observer observer = new AtomicObserver(Observer, subscription); + + for (T item : iterable) { + observer.onNext(item); + } + observer.onCompleted(); + + return subscription; + } + } + + public static class UnitTest { + + @Test + public void testIterable() { + Observable Observable = toObservableIterable(Arrays. asList("one", "two", "three")); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + Observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + } + } +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationToObservableList.java b/rxjava-core/src/main/java/org/rx/operations/OperationToObservableList.java new file mode 100644 index 0000000000..e8ac558d92 --- /dev/null +++ b/rxjava-core/src/main/java/org/rx/operations/OperationToObservableList.java @@ -0,0 +1,84 @@ +package org.rx.operations; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.junit.Test; +import org.mockito.Mockito; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; + +public final class OperationToObservableList { + + public static Observable> toObservableList(Observable that) { + return new ToObservableList(that); + } + + private static class ToObservableList extends Observable> { + + private final Observable that; + final ConcurrentLinkedQueue list = new ConcurrentLinkedQueue(); + + public ToObservableList(Observable that) { + this.that = that; + } + + public Subscription subscribe(Observer> listObserver) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + final Observer> Observer = new AtomicObserver>(listObserver, subscription); + + subscription.setActual(that.subscribe(new Observer() { + public void onNext(T value) { + // onNext can be concurrently executed so list must be thread-safe + list.add(value); + } + + public void onError(Exception ex) { + Observer.onError(ex); + } + + public void onCompleted() { + try { + // copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface + ArrayList l = new ArrayList(list.size()); + for (T t : list) { + l.add(t); + } + + // benjchristensen => I want to make this immutable but some clients are sorting this + // instead of using toSortedList() and this change breaks them until we migrate their code. + // Observer.onNext(Collections.unmodifiableList(l)); + Observer.onNext(l); + Observer.onCompleted(); + } catch (Exception e) { + onError(e); + } + + } + })); + return subscription; + } + } + + public static class UnitTest { + + @Test + public void testList() { + Observable w = Observable.toObservable("one", "two", "three"); + Observable> Observable = toObservableList(w); + + @SuppressWarnings("unchecked") + Observer> aObserver = mock(Observer.class); + Observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(Arrays.asList("one", "two", "three")); + verify(aObserver, Mockito.never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + } + } +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationToObservableSortedList.java b/rxjava-core/src/main/java/org/rx/operations/OperationToObservableSortedList.java new file mode 100644 index 0000000000..29e5f4ed67 --- /dev/null +++ b/rxjava-core/src/main/java/org/rx/operations/OperationToObservableSortedList.java @@ -0,0 +1,166 @@ +package org.rx.operations; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.junit.Test; +import org.mockito.Mockito; +import org.rx.functions.Func2; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; + +/** + * Similar to toList in that it converts a sequence into a List except that it accepts a Function that will provide an implementation of Comparator. + * + * @param + */ +public final class OperationToObservableSortedList { + + /** + * Sort T objects by their natural order (object must implement Comparable). + * + * @param sequence + * @throws ClassCastException + * if T objects do not implement Comparable + * @return + */ + public static Observable> toSortedList(Observable sequence) { + return new ToObservableSortedList(sequence); + } + + /** + * Sort T objects using the defined sort function. + * + * @param sequence + * @param sortFunction + * @return + */ + public static Observable> toSortedList(Observable sequence, Func2 sortFunction) { + return new ToObservableSortedList(sequence, sortFunction); + } + + private static class ToObservableSortedList extends Observable> { + + private final Observable that; + private final ConcurrentLinkedQueue list = new ConcurrentLinkedQueue(); + private final Func2 sortFunction; + + // unchecked as we're support Object for the default + @SuppressWarnings("unchecked") + private ToObservableSortedList(Observable that) { + this(that, defaultSortFunction); + } + + private ToObservableSortedList(Observable that, Func2 sortFunction) { + this.that = that; + this.sortFunction = sortFunction; + } + + public Subscription subscribe(Observer> listObserver) { + final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); + final Observer> Observer = new AtomicObserver>(listObserver, subscription); + + subscription.setActual(that.subscribe(new Observer() { + public void onNext(T value) { + // onNext can be concurrently executed so list must be thread-safe + list.add(value); + } + + public void onError(Exception ex) { + Observer.onError(ex); + } + + public void onCompleted() { + try { + // copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface + ArrayList l = new ArrayList(list.size()); + for (T t : list) { + l.add(t); + } + + // sort the list before delivery + Collections.sort(l, new Comparator() { + + @Override + public int compare(T o1, T o2) { + return sortFunction.call(o1, o2); + } + + }); + + Observer.onNext(Collections.unmodifiableList(l)); + Observer.onCompleted(); + } catch (Exception e) { + onError(e); + } + + } + })); + return subscription; + } + + // raw because we want to support Object for this default + @SuppressWarnings("rawtypes") + private static Func2 defaultSortFunction = new DefaultComparableFunction(); + + private static class DefaultComparableFunction implements Func2 { + + // unchecked because we want to support Object for this default + @SuppressWarnings("unchecked") + @Override + public Integer call(Object t1, Object t2) { + Comparable c1 = (Comparable) t1; + Comparable c2 = (Comparable) t2; + return c1.compareTo(c2); + } + + } + + } + + public static class UnitTest { + + @Test + public void testSortedList() { + Observable w = Observable.toObservable(1, 3, 2, 5, 4); + Observable> Observable = toSortedList(w); + + @SuppressWarnings("unchecked") + Observer> aObserver = mock(Observer.class); + Observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(Arrays.asList(1, 2, 3, 4, 5)); + verify(aObserver, Mockito.never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testSortedListWithCustomFunction() { + Observable w = Observable.toObservable(1, 3, 2, 5, 4); + Observable> Observable = toSortedList(w, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t2 - t1; + } + + }); + ; + + @SuppressWarnings("unchecked") + Observer> aObserver = mock(Observer.class); + Observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(Arrays.asList(5, 4, 3, 2, 1)); + verify(aObserver, Mockito.never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + } + + } +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableFunction.java b/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableFunction.java deleted file mode 100644 index 07ed8ad722..0000000000 --- a/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableFunction.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.rx.operations; - -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import org.junit.Test; -import org.rx.functions.Func1; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - - -/** - * Accepts a Function and makes it into a Watchable. - *

          - * This is equivalent to Rx Observable.Create - * - * @see http://msdn.microsoft.com/en-us/library/hh229114(v=vs.103).aspx - * @see WatchableExtensions.toWatchable - * @see WatchableExtensions.create - */ -/* package */ class OperationToWatchableFunction extends AbstractIObservable { - private final Func1> func; - - OperationToWatchableFunction(Func1> func) { - this.func = func; - } - - @Override - public IDisposable subscribe(IObserver watcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - // We specifically use the SingleThreaded AtomicWatcher since we can't ensure the implementation is thread-safe - // so will not allow it to use the MultiThreaded version even when other operators are doing so - final IObserver atomicWatcher = new AtomicWatcherSingleThreaded(watcher, subscription); - // if func.call is synchronous, then the subscription won't matter as it can't ever be called - // if func.call is asynchronous, then the subscription will get set and can be unsubscribed from - subscription.setActual(func.call(atomicWatcher)); - - return subscription; - } - - public static class UnitTest { - - @Test - public void testCreate() { - - IObservable watchable = new OperationToWatchableFunction(new Func1>() { - - @Override - public IDisposable call(IObserver watcher) { - watcher.onNext("one"); - watcher.onNext("two"); - watcher.onNext("three"); - watcher.onCompleted(); - return WatchableExtensions.noOpSubscription(); - } - - }); - - @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); - verify(aWatcher, times(1)).onNext("one"); - verify(aWatcher, times(1)).onNext("two"); - verify(aWatcher, times(1)).onNext("three"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - } - } -} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableIterable.java b/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableIterable.java deleted file mode 100644 index afd5fe0d3c..0000000000 --- a/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableIterable.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.rx.operations; - -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; - -import org.junit.Test; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - - -/** - * Accepts an Iterable object and exposes it as an Observable. - * - * @param - * The type of the Iterable sequence. - */ -/* package */class OperationToWatchableIterable extends AbstractIObservable { - public OperationToWatchableIterable(Iterable list) { - this.iterable = list; - } - - public Iterable iterable; - - public IDisposable subscribe(IObserver watcher) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(WatchableExtensions.noOpSubscription()); - final IObserver observer = new AtomicWatcher(watcher, subscription); - - for (T item : iterable) { - observer.onNext(item); - } - observer.onCompleted(); - - return subscription; - } - - public static class UnitTest { - - @Test - public void testIterable() { - IObservable watchable = new OperationToWatchableIterable(Arrays. asList("one", "two", "three")); - - @SuppressWarnings("unchecked") - IObserver aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); - verify(aWatcher, times(1)).onNext("one"); - verify(aWatcher, times(1)).onNext("two"); - verify(aWatcher, times(1)).onNext("three"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - } - } -} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableList.java b/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableList.java deleted file mode 100644 index 7ecb808c23..0000000000 --- a/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableList.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.rx.operations; - -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.junit.Test; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - - -final class OperationToWatchableList extends AbstractIObservable> { - private final IObservable that; - final ConcurrentLinkedQueue list = new ConcurrentLinkedQueue(); - - OperationToWatchableList(IObservable that) { - this.that = that; - } - - public IDisposable subscribe(IObserver> listObserver) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - final IObserver> watcher = new AtomicWatcher>(listObserver, subscription); - - subscription.setActual(that.subscribe(new IObserver() { - public void onNext(T value) { - // onNext can be concurrently executed so list must be thread-safe - list.add(value); - } - - public void onError(Exception ex) { - watcher.onError(ex); - } - - public void onCompleted() { - try { - // copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface - ArrayList l = new ArrayList(list.size()); - for (T t : list) { - l.add(t); - } - - // benjchristensen => I want to make this immutable but some clients are sorting this - // instead of using toSortedList() and this change breaks them until we migrate their code. - // watcher.onNext(Collections.unmodifiableList(l)); - watcher.onNext(l); - watcher.onCompleted(); - } catch (Exception e) { - onError(e); - } - - } - })); - return subscription; - } - - public static class UnitTest { - - @Test - public void testList() { - IObservable w = WatchableExtensions.toWatchable("one", "two", "three"); - IObservable> watchable = new OperationToWatchableList(w); - - @SuppressWarnings("unchecked") - IObserver> aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); - verify(aWatcher, times(1)).onNext(Arrays.asList("one", "two", "three")); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - } - } -} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableSortedList.java b/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableSortedList.java deleted file mode 100644 index d005d44dff..0000000000 --- a/rxjava-core/src/main/java/org/rx/operations/OperationToWatchableSortedList.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.rx.operations; - -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.junit.Test; -import org.rx.functions.Func2; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - - -/** - * Similar to toList in that it converts a sequence into a List except that it accepts a Function that will provide an implementation of Comparator. - * - * @param - */ -final class OperationToWatchableSortedList extends AbstractIObservable> { - - /** - * Sort T objects by their natural order (object must implement Comparable). - * - * @param sequence - * @throws ClassCastException - * if T objects do not implement Comparable - * @return - */ - public static IObservable> toSortedList(IObservable sequence) { - return new OperationToWatchableSortedList(sequence); - } - - /** - * Sort T objects using the defined sort function. - * - * @param sequence - * @param sortFunction - * @return - */ - public static IObservable> toSortedList(IObservable sequence, Func2 sortFunction) { - return new OperationToWatchableSortedList(sequence, sortFunction); - } - - private final IObservable that; - private final ConcurrentLinkedQueue list = new ConcurrentLinkedQueue(); - private final Func2 sortFunction; - - // unchecked as we're support Object for the default - @SuppressWarnings("unchecked") - private OperationToWatchableSortedList(IObservable that) { - this(that, defaultSortFunction); - } - - private OperationToWatchableSortedList(IObservable that, Func2 sortFunction) { - this.that = that; - this.sortFunction = sortFunction; - } - - public IDisposable subscribe(IObserver> listObserver) { - final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); - final IObserver> watcher = new AtomicWatcher>(listObserver, subscription); - - subscription.setActual(that.subscribe(new IObserver() { - public void onNext(T value) { - // onNext can be concurrently executed so list must be thread-safe - list.add(value); - } - - public void onError(Exception ex) { - watcher.onError(ex); - } - - public void onCompleted() { - try { - // copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface - ArrayList l = new ArrayList(list.size()); - for (T t : list) { - l.add(t); - } - - // sort the list before delivery - Collections.sort(l, new Comparator() { - - @Override - public int compare(T o1, T o2) { - return sortFunction.call(o1, o2); - } - - }); - - watcher.onNext(Collections.unmodifiableList(l)); - watcher.onCompleted(); - } catch (Exception e) { - onError(e); - } - - } - })); - return subscription; - } - - // raw because we want to support Object for this default - @SuppressWarnings("rawtypes") - private static Func2 defaultSortFunction = new DefaultComparableFunction(); - - private static class DefaultComparableFunction implements Func2 { - - // unchecked because we want to support Object for this default - @SuppressWarnings("unchecked") - @Override - public Integer call(Object t1, Object t2) { - Comparable c1 = (Comparable) t1; - Comparable c2 = (Comparable) t2; - return c1.compareTo(c2); - } - - } - - public static class UnitTest { - - @Test - public void testSortedList() { - IObservable w = WatchableExtensions.toWatchable(1, 3, 2, 5, 4); - IObservable> watchable = toSortedList(w); - - @SuppressWarnings("unchecked") - IObserver> aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); - verify(aWatcher, times(1)).onNext(Arrays.asList(1, 2, 3, 4, 5)); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - } - - @Test - public void testSortedListWithCustomFunction() { - IObservable w = WatchableExtensions.toWatchable(1, 3, 2, 5, 4); - IObservable> watchable = toSortedList(w, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t2 - t1; - } - - }); - ; - - @SuppressWarnings("unchecked") - IObserver> aWatcher = mock(IObserver.class); - watchable.subscribe(aWatcher); - verify(aWatcher, times(1)).onNext(Arrays.asList(5, 4, 3, 2, 1)); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - } - - } -} \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/operations/OperationZip.java b/rxjava-core/src/main/java/org/rx/operations/OperationZip.java index ed1cb397fa..f453ba9abd 100644 --- a/rxjava-core/src/main/java/org/rx/operations/OperationZip.java +++ b/rxjava-core/src/main/java/org/rx/operations/OperationZip.java @@ -18,46 +18,44 @@ import org.rx.functions.Func4; import org.rx.functions.FuncN; import org.rx.functions.Functions; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; +import org.rx.reactive.Observable; +import org.rx.reactive.Observer; +import org.rx.reactive.Subscription; +public final class OperationZip { -class OperationZip { - - public static IObservable zip(IObservable w0, IObservable w1, Func2 zipFunction) { + public static Observable zip(Observable w0, Observable w1, Func2 zipFunction) { Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addWatcher(new ZipWatcher(a, w0)); - a.addWatcher(new ZipWatcher(a, w1)); + a.addObserver(new ZipObserver(a, w0)); + a.addObserver(new ZipObserver(a, w1)); return a; } - public static IObservable zip(IObservable w0, IObservable w1, IObservable w2, Func3 zipFunction) { + public static Observable zip(Observable w0, Observable w1, Observable w2, Func3 zipFunction) { Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addWatcher(new ZipWatcher(a, w0)); - a.addWatcher(new ZipWatcher(a, w1)); - a.addWatcher(new ZipWatcher(a, w2)); + a.addObserver(new ZipObserver(a, w0)); + a.addObserver(new ZipObserver(a, w1)); + a.addObserver(new ZipObserver(a, w2)); return a; } - public static IObservable zip(IObservable w0, IObservable w1, IObservable w2, IObservable w3, Func4 zipFunction) { + public static Observable zip(Observable w0, Observable w1, Observable w2, Observable w3, Func4 zipFunction) { Aggregator a = new Aggregator(Functions.fromFunc(zipFunction)); - a.addWatcher(new ZipWatcher(a, w0)); - a.addWatcher(new ZipWatcher(a, w1)); - a.addWatcher(new ZipWatcher(a, w2)); - a.addWatcher(new ZipWatcher(a, w3)); + a.addObserver(new ZipObserver(a, w0)); + a.addObserver(new ZipObserver(a, w1)); + a.addObserver(new ZipObserver(a, w2)); + a.addObserver(new ZipObserver(a, w3)); return a; } @ThreadSafe - private static class ZipWatcher implements IObserver { - final IObservable w; + private static class ZipObserver implements Observer { + final Observable w; final Aggregator a; - private final AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); + private final AtomicObservableSubscription subscription = new AtomicObservableSubscription(); private final AtomicBoolean subscribed = new AtomicBoolean(false); - public ZipWatcher(Aggregator a, IObservable w) { + public ZipObserver(Aggregator a, Observable w) { this.a = a; this.w = w; } @@ -90,84 +88,84 @@ public void onNext(T args) { } /** - * Receive notifications from each of the Watchables we are reducing and execute the zipFunction whenever we have received events from all Watchables. + * Receive notifications from each of the Observables we are reducing and execute the zipFunction whenever we have received events from all Observables. * * @param */ @ThreadSafe - private static class Aggregator extends AbstractIObservable { + private static class Aggregator extends Observable { private final FuncN zipFunction; - private volatile AtomicWatcher watcher = null; - private volatile AtomicWatchableSubscription subscription = new AtomicWatchableSubscription(); + private volatile AtomicObserver Observer = null; + private volatile AtomicObservableSubscription subscription = new AtomicObservableSubscription(); private AtomicBoolean started = new AtomicBoolean(false); private AtomicBoolean running = new AtomicBoolean(true); - private ConcurrentHashMap, Boolean> completed = new ConcurrentHashMap, Boolean>(); + private ConcurrentHashMap, Boolean> completed = new ConcurrentHashMap, Boolean>(); /* we use ConcurrentHashMap despite synchronization of methods because stop() does NOT use synchronization and this map is used by it and can be called by other threads */ - private ConcurrentHashMap, ConcurrentLinkedQueue> receivedValuesPerWatcher = new ConcurrentHashMap, ConcurrentLinkedQueue>(); - /* we use a ConcurrentLinkedQueue to retain ordering (I'd like to just use a ConcurrentLinkedHashMap for 'receivedValuesPerWatcher' but that doesn't exist in standard java */ - private ConcurrentLinkedQueue> watchers = new ConcurrentLinkedQueue>(); + private ConcurrentHashMap, ConcurrentLinkedQueue> receivedValuesPerObserver = new ConcurrentHashMap, ConcurrentLinkedQueue>(); + /* we use a ConcurrentLinkedQueue to retain ordering (I'd like to just use a ConcurrentLinkedHashMap for 'receivedValuesPerObserver' but that doesn't exist in standard java */ + private ConcurrentLinkedQueue> Observers = new ConcurrentLinkedQueue>(); public Aggregator(FuncN zipFunction) { this.zipFunction = zipFunction; } /** - * Receive notification of a watcher starting (meaning we should require it for aggregation) + * Receive notification of a Observer starting (meaning we should require it for aggregation) * * @param w */ @GuardedBy("Invoked ONLY from the static factory methods at top of this class which are always an atomic execution by a single thread.") - private void addWatcher(ZipWatcher w) { - // initialize this ZipWatcher - watchers.add(w); - receivedValuesPerWatcher.put(w, new ConcurrentLinkedQueue()); + private void addObserver(ZipObserver w) { + // initialize this ZipObserver + Observers.add(w); + receivedValuesPerObserver.put(w, new ConcurrentLinkedQueue()); } /** - * Receive notification of a watcher completing its iterations. + * Receive notification of a Observer completing its iterations. * * @param w */ - void complete(ZipWatcher w) { - // store that this ZipWatcher is completed + void complete(ZipObserver w) { + // store that this ZipObserver is completed completed.put(w, Boolean.TRUE); - // if all ZipWatchers are completed, we mark the whole thing as completed - if (completed.size() == watchers.size()) { + // if all ZipObservers are completed, we mark the whole thing as completed + if (completed.size() == Observers.size()) { if (running.compareAndSet(true, false)) { // this thread succeeded in setting running=false so let's propagate the completion // mark ourselves as done - watcher.onCompleted(); + Observer.onCompleted(); } } } /** - * Receive error for a watcher. Throw the error up the chain and stop processing. + * Receive error for a Observer. Throw the error up the chain and stop processing. * * @param w */ - void error(ZipWatcher w, Exception e) { + void error(ZipObserver w, Exception e) { if (running.compareAndSet(true, false)) { // this thread succeeded in setting running=false so let's propagate the error - watcher.onError(e); + Observer.onError(e); /* since we receive an error we want to tell everyone to stop */ stop(); } } /** - * Receive the next value from a watcher. + * Receive the next value from a Observer. *

          - * If we have received values from all watchers, trigger the zip function, otherwise store the value and keep waiting. + * If we have received values from all Observers, trigger the zip function, otherwise store the value and keep waiting. * * @param w * @param arg */ - void next(ZipWatcher w, Object arg) { - if (watcher == null) { - throw new RuntimeException("This shouldn't be running if a watcher isn't registered"); + void next(ZipObserver w, Object arg) { + if (Observer == null) { + throw new RuntimeException("This shouldn't be running if a Observer isn't registered"); } /* if we've been 'unsubscribed' don't process anything further even if the things we're watching keep sending (likely because they are not responding to the unsubscribe call) */ @@ -175,43 +173,43 @@ void next(ZipWatcher w, Object arg) { return; } - // store the value we received and below we'll decide if we are to send it to the watcher - receivedValuesPerWatcher.get(w).add(arg); + // store the value we received and below we'll decide if we are to send it to the Observer + receivedValuesPerObserver.get(w).add(arg); // define here so the variable is out of the synchronized scope - Object[] argsToZip = new Object[watchers.size()]; + Object[] argsToZip = new Object[Observers.size()]; /* we have to synchronize here despite using concurrent data structures because the compound logic here must all be done atomically */ synchronized (this) { - // if all ZipWatchers in 'receivedValues' map have a value, invoke the zipFunction - for (ZipWatcher rw : receivedValuesPerWatcher.keySet()) { - if (receivedValuesPerWatcher.get(rw).peek() == null) { + // if all ZipObservers in 'receivedValues' map have a value, invoke the zipFunction + for (ZipObserver rw : receivedValuesPerObserver.keySet()) { + if (receivedValuesPerObserver.get(rw).peek() == null) { // we have a null meaning the queues aren't all populated so won't do anything return; } } // if we get to here this means all the queues have data int i = 0; - for (ZipWatcher rw : watchers) { - argsToZip[i++] = receivedValuesPerWatcher.get(rw).remove(); + for (ZipObserver rw : Observers) { + argsToZip[i++] = receivedValuesPerObserver.get(rw).remove(); } } // if we did not return above from the synchronized block we can now invoke the zipFunction with all of the args // we do this outside the synchronized block as it is now safe to call this concurrently and don't need to block other threads from calling // this 'next' method while another thread finishes calling this zipFunction - watcher.onNext(zipFunction.call(argsToZip)); + Observer.onNext(zipFunction.call(argsToZip)); } @Override - public IDisposable subscribe(IObserver watcher) { + public Subscription subscribe(Observer Observer) { if (started.compareAndSet(false, true)) { - this.watcher = new AtomicWatcher(watcher, subscription); - /* start the watchers */ - for (ZipWatcher rw : watchers) { + this.Observer = new AtomicObserver(Observer, subscription); + /* start the Observers */ + for (ZipObserver rw : Observers) { rw.startWatching(); } - return subscription.setActual(new IDisposable() { + return subscription.setActual(new Subscription() { @Override public void unsubscribe() { @@ -220,8 +218,8 @@ public void unsubscribe() { }); } else { - /* a watcher already has subscribed so blow up */ - throw new IllegalStateException("Only one watcher can subscribe to this Watchable."); + /* a Observer already has subscribed so blow up */ + throw new IllegalStateException("Only one Observer can subscribe to this Observable."); } } @@ -229,20 +227,20 @@ public void unsubscribe() { * Do NOT synchronize this because it gets called via unsubscribe which can occur on other threads * and result in deadlocks. (http://jira/browse/API-4060) * - * AtomicWatchableSubscription uses compareAndSet instead of locking to avoid deadlocks but ensure single-execution. + * AtomicObservableSubscription uses compareAndSet instead of locking to avoid deadlocks but ensure single-execution. * * We do the same in the implementation of this method. * * ThreadSafety of this method is provided by: * - AtomicBoolean[running].compareAndSet - * - ConcurrentLinkedQueue[watchers] - * - ZipWatcher.subscription being an AtomicWatchableSubscription + * - ConcurrentLinkedQueue[Observers] + * - ZipObserver.subscription being an AtomicObservableSubscription */ private void stop() { /* tell ourselves to stop processing onNext events by setting running=false */ if (running.compareAndSet(true, false)) { - /* propogate to all watchers to unsubscribe if this thread succeeded in setting running=false */ - for (ZipWatcher rw : watchers) { + /* propogate to all Observers to unsubscribe if this thread succeeded in setting running=false */ + for (ZipObserver rw : Observers) { if (rw.subscription != null) { rw.subscription.unsubscribe(); } @@ -257,32 +255,32 @@ public static class UnitTest { @SuppressWarnings("unchecked") /* mock calls don't do generics */ @Test - public void testZippingDifferentLengthWatchableSequences1() { - IObserver w = mock(IObserver.class); + public void testZippingDifferentLengthObservableSequences1() { + Observer w = mock(Observer.class); - TestWatchable w1 = new TestWatchable(); - TestWatchable w2 = new TestWatchable(); - TestWatchable w3 = new TestWatchable(); + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); - IObservable zipW = zip(w1, w2, w3, getConcat3StringsZipr()); + Observable zipW = zip(w1, w2, w3, getConcat3StringsZipr()); zipW.subscribe(w); /* simulate sending data */ // once for w1 - w1.watcher.onNext("1a"); - w1.watcher.onCompleted(); + w1.Observer.onNext("1a"); + w1.Observer.onCompleted(); // twice for w2 - w2.watcher.onNext("2a"); - w2.watcher.onNext("2b"); - w2.watcher.onCompleted(); + w2.Observer.onNext("2a"); + w2.Observer.onNext("2b"); + w2.Observer.onCompleted(); // 4 times for w3 - w3.watcher.onNext("3a"); - w3.watcher.onNext("3b"); - w3.watcher.onNext("3c"); - w3.watcher.onNext("3d"); - w3.watcher.onCompleted(); + w3.Observer.onNext("3a"); + w3.Observer.onNext("3b"); + w3.Observer.onNext("3c"); + w3.Observer.onNext("3d"); + w3.Observer.onCompleted(); - /* we should have been called 1 time on the watcher */ + /* we should have been called 1 time on the Observer */ InOrder inOrder = inOrder(w); inOrder.verify(w).onNext("1a2a3a"); @@ -290,33 +288,33 @@ public void testZippingDifferentLengthWatchableSequences1() { } @Test - public void testZippingDifferentLengthWatchableSequences2() { + public void testZippingDifferentLengthObservableSequences2() { @SuppressWarnings("unchecked") - IObserver w = mock(IObserver.class); + Observer w = mock(Observer.class); - TestWatchable w1 = new TestWatchable(); - TestWatchable w2 = new TestWatchable(); - TestWatchable w3 = new TestWatchable(); + TestObservable w1 = new TestObservable(); + TestObservable w2 = new TestObservable(); + TestObservable w3 = new TestObservable(); - IObservable zipW = zip(w1, w2, w3, getConcat3StringsZipr()); + Observable zipW = zip(w1, w2, w3, getConcat3StringsZipr()); zipW.subscribe(w); /* simulate sending data */ // 4 times for w1 - w1.watcher.onNext("1a"); - w1.watcher.onNext("1b"); - w1.watcher.onNext("1c"); - w1.watcher.onNext("1d"); - w1.watcher.onCompleted(); + w1.Observer.onNext("1a"); + w1.Observer.onNext("1b"); + w1.Observer.onNext("1c"); + w1.Observer.onNext("1d"); + w1.Observer.onCompleted(); // twice for w2 - w2.watcher.onNext("2a"); - w2.watcher.onNext("2b"); - w2.watcher.onCompleted(); + w2.Observer.onNext("2a"); + w2.Observer.onNext("2b"); + w2.Observer.onCompleted(); // 1 times for w3 - w3.watcher.onNext("3a"); - w3.watcher.onCompleted(); + w3.Observer.onNext("3a"); + w3.Observer.onCompleted(); - /* we should have been called 1 time on the watcher */ + /* we should have been called 1 time on the Observer */ InOrder inOrder = inOrder(w); inOrder.verify(w).onNext("1a2a3a"); @@ -332,43 +330,43 @@ public void testZippingDifferentLengthWatchableSequences2() { @Test public void testAggregatorSimple() { FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all watchables provide values */ + /* create the aggregator which will execute the zip function when all Observables provide values */ Aggregator a = new Aggregator(zipr); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - ZipWatcher r1 = mock(ZipWatcher.class); - ZipWatcher r2 = mock(ZipWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, "world"); - InOrder inOrder = inOrder(aWatcher); + InOrder inOrder = inOrder(aObserver); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - inOrder.verify(aWatcher, times(1)).onNext("helloworld"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); a.next(r1, "hello "); a.next(r2, "again"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - inOrder.verify(aWatcher, times(1)).onNext("hello again"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("hello again"); a.complete(r1); a.complete(r2); - inOrder.verify(aWatcher, never()).onNext(anyString()); - verify(aWatcher, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); + verify(aObserver, times(1)).onCompleted(); } @SuppressWarnings("unchecked") @@ -376,38 +374,38 @@ public void testAggregatorSimple() { @Test public void testAggregatorDifferentSizedResultsWithOnComplete() { FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all watchables provide values */ + /* create the aggregator which will execute the zip function when all Observables provide values */ Aggregator a = new Aggregator(zipr); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - ZipWatcher r1 = mock(ZipWatcher.class); - ZipWatcher r2 = mock(ZipWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, "world"); a.complete(r2); - InOrder inOrder = inOrder(aWatcher); + InOrder inOrder = inOrder(aObserver); - inOrder.verify(aWatcher, never()).onError(any(Exception.class)); - inOrder.verify(aWatcher, never()).onCompleted(); - inOrder.verify(aWatcher, times(1)).onNext("helloworld"); + inOrder.verify(aObserver, never()).onError(any(Exception.class)); + inOrder.verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); a.next(r1, "hi"); a.complete(r1); - inOrder.verify(aWatcher, never()).onError(any(Exception.class)); - inOrder.verify(aWatcher, times(1)).onCompleted(); - inOrder.verify(aWatcher, never()).onNext(anyString()); + inOrder.verify(aObserver, never()).onError(any(Exception.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); } @SuppressWarnings("unchecked") @@ -415,38 +413,38 @@ public void testAggregatorDifferentSizedResultsWithOnComplete() { @Test public void testAggregateMultipleTypes() { FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all watchables provide values */ + /* create the aggregator which will execute the zip function when all Observables provide values */ Aggregator a = new Aggregator(zipr); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - ZipWatcher r1 = mock(ZipWatcher.class); - ZipWatcher r2 = mock(ZipWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, "world"); a.complete(r2); - InOrder inOrder = inOrder(aWatcher); + InOrder inOrder = inOrder(aObserver); - inOrder.verify(aWatcher, never()).onError(any(Exception.class)); - inOrder.verify(aWatcher, never()).onCompleted(); - inOrder.verify(aWatcher, times(1)).onNext("helloworld"); + inOrder.verify(aObserver, never()).onError(any(Exception.class)); + inOrder.verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("helloworld"); a.next(r1, "hi"); a.complete(r1); - inOrder.verify(aWatcher, never()).onError(any(Exception.class)); - inOrder.verify(aWatcher, times(1)).onCompleted(); - inOrder.verify(aWatcher, never()).onNext(anyString()); + inOrder.verify(aObserver, never()).onError(any(Exception.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); } @SuppressWarnings("unchecked") @@ -454,31 +452,31 @@ public void testAggregateMultipleTypes() { @Test public void testAggregate3Types() { FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all watchables provide values */ + /* create the aggregator which will execute the zip function when all Observables provide values */ Aggregator a = new Aggregator(zipr); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - ZipWatcher r1 = mock(ZipWatcher.class); - ZipWatcher r2 = mock(ZipWatcher.class); - ZipWatcher r3 = mock(ZipWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); + ZipObserver r3 = mock(ZipObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); - a.addWatcher(r3); + a.addObserver(r1); + a.addObserver(r2); + a.addObserver(r3); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, 2); a.next(r3, new int[] { 5, 6, 7 }); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("hello2[5, 6, 7]"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("hello2[5, 6, 7]"); } @SuppressWarnings("unchecked") @@ -486,45 +484,45 @@ public void testAggregate3Types() { @Test public void testAggregatorsWithDifferentSizesAndTiming() { FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all watchables provide values */ + /* create the aggregator which will execute the zip function when all Observables provide values */ Aggregator a = new Aggregator(zipr); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - ZipWatcher r1 = mock(ZipWatcher.class); - ZipWatcher r2 = mock(ZipWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "one"); a.next(r1, "two"); a.next(r1, "three"); a.next(r2, "A"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("oneA"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("oneA"); a.next(r1, "four"); a.complete(r1); a.next(r2, "B"); - verify(aWatcher, times(1)).onNext("twoB"); + verify(aObserver, times(1)).onNext("twoB"); a.next(r2, "C"); - verify(aWatcher, times(1)).onNext("threeC"); + verify(aObserver, times(1)).onNext("threeC"); a.next(r2, "D"); - verify(aWatcher, times(1)).onNext("fourD"); + verify(aObserver, times(1)).onNext("fourD"); a.next(r2, "E"); - verify(aWatcher, never()).onNext("E"); + verify(aObserver, never()).onNext("E"); a.complete(r2); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); } @SuppressWarnings("unchecked") @@ -532,37 +530,37 @@ public void testAggregatorsWithDifferentSizesAndTiming() { @Test public void testAggregatorError() { FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all watchables provide values */ + /* create the aggregator which will execute the zip function when all Observables provide values */ Aggregator a = new Aggregator(zipr); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - ZipWatcher r1 = mock(ZipWatcher.class); - ZipWatcher r2 = mock(ZipWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, "world"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("helloworld"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); a.error(r1, new RuntimeException("")); a.next(r1, "hello"); a.next(r2, "again"); - verify(aWatcher, times(1)).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); + verify(aObserver, times(1)).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); // we don't want to be called again after an error - verify(aWatcher, times(0)).onNext("helloagain"); + verify(aObserver, times(0)).onNext("helloagain"); } @SuppressWarnings("unchecked") @@ -570,37 +568,37 @@ public void testAggregatorError() { @Test public void testAggregatorUnsubscribe() { FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all watchables provide values */ + /* create the aggregator which will execute the zip function when all Observables provide values */ Aggregator a = new Aggregator(zipr); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - IDisposable subscription = a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + Subscription subscription = a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - ZipWatcher r1 = mock(ZipWatcher.class); - ZipWatcher r2 = mock(ZipWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "hello"); a.next(r2, "world"); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); - verify(aWatcher, times(1)).onNext("helloworld"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); + verify(aObserver, times(1)).onNext("helloworld"); subscription.unsubscribe(); a.next(r1, "hello"); a.next(r2, "again"); - verify(aWatcher, times(0)).onError(any(Exception.class)); - verify(aWatcher, never()).onCompleted(); + verify(aObserver, times(0)).onError(any(Exception.class)); + verify(aObserver, never()).onCompleted(); // we don't want to be called again after an error - verify(aWatcher, times(0)).onNext("helloagain"); + verify(aObserver, times(0)).onNext("helloagain"); } @SuppressWarnings("unchecked") @@ -608,38 +606,38 @@ public void testAggregatorUnsubscribe() { @Test public void testAggregatorEarlyCompletion() { FuncN zipr = getConcatZipr(); - /* create the aggregator which will execute the zip function when all watchables provide values */ + /* create the aggregator which will execute the zip function when all Observables provide values */ Aggregator a = new Aggregator(zipr); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); - a.subscribe(aWatcher); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); + a.subscribe(aObserver); - /* mock the Watchable Watchers that are 'pushing' data for us */ - ZipWatcher r1 = mock(ZipWatcher.class); - ZipWatcher r2 = mock(ZipWatcher.class); + /* mock the Observable Observers that are 'pushing' data for us */ + ZipObserver r1 = mock(ZipObserver.class); + ZipObserver r2 = mock(ZipObserver.class); /* pretend we're starting up */ - a.addWatcher(r1); - a.addWatcher(r2); + a.addObserver(r1); + a.addObserver(r2); - /* simulate the watchables pushing data into the aggregator */ + /* simulate the Observables pushing data into the aggregator */ a.next(r1, "one"); a.next(r1, "two"); a.complete(r1); a.next(r2, "A"); - InOrder inOrder = inOrder(aWatcher); + InOrder inOrder = inOrder(aObserver); - inOrder.verify(aWatcher, never()).onError(any(Exception.class)); - inOrder.verify(aWatcher, never()).onCompleted(); - inOrder.verify(aWatcher, times(1)).onNext("oneA"); + inOrder.verify(aObserver, never()).onError(any(Exception.class)); + inOrder.verify(aObserver, never()).onCompleted(); + inOrder.verify(aObserver, times(1)).onNext("oneA"); a.complete(r2); - inOrder.verify(aWatcher, never()).onError(any(Exception.class)); - inOrder.verify(aWatcher, times(1)).onCompleted(); - inOrder.verify(aWatcher, never()).onNext(anyString()); + inOrder.verify(aObserver, never()).onError(any(Exception.class)); + inOrder.verify(aObserver, times(1)).onCompleted(); + inOrder.verify(aObserver, never()).onNext(anyString()); } @SuppressWarnings("unchecked") @@ -648,17 +646,17 @@ public void testAggregatorEarlyCompletion() { public void testZip2Types() { Func2 zipr = getConcatStringIntegerZipr(); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); - IObservable w = zip(WatchableExtensions.toWatchable("one", "two"), WatchableExtensions.toWatchable(2, 3, 4), zipr); - w.subscribe(aWatcher); + Observable w = zip(Observable.toObservable("one", "two"), Observable.toObservable(2, 3, 4), zipr); + w.subscribe(aObserver); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - verify(aWatcher, times(1)).onNext("one2"); - verify(aWatcher, times(1)).onNext("two3"); - verify(aWatcher, never()).onNext("4"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one2"); + verify(aObserver, times(1)).onNext("two3"); + verify(aObserver, never()).onNext("4"); } @SuppressWarnings("unchecked") @@ -667,28 +665,29 @@ public void testZip2Types() { public void testZip3Types() { Func3 zipr = getConcatStringIntegerIntArrayZipr(); - /* define a Watcher to receive aggregated events */ - IObserver aWatcher = mock(IObserver.class); + /* define a Observer to receive aggregated events */ + Observer aObserver = mock(Observer.class); - IObservable w = zip(WatchableExtensions.toWatchable("one", "two"), WatchableExtensions.toWatchable(2), WatchableExtensions.toWatchable(new int[] { 4, 5, 6 }), zipr); - w.subscribe(aWatcher); + Observable w = zip(Observable.toObservable("one", "two"), Observable.toObservable(2), Observable.toObservable(new int[] { 4, 5, 6 }), zipr); + w.subscribe(aObserver); - verify(aWatcher, never()).onError(any(Exception.class)); - verify(aWatcher, times(1)).onCompleted(); - verify(aWatcher, times(1)).onNext("one2[4, 5, 6]"); - verify(aWatcher, never()).onNext("two"); + verify(aObserver, never()).onError(any(Exception.class)); + verify(aObserver, times(1)).onCompleted(); + verify(aObserver, times(1)).onNext("one2[4, 5, 6]"); + verify(aObserver, never()).onNext("two"); } @Test public void testOnNextExceptionInvokesOnError() { Func2 zipr = getDivideZipr(); - IObserver aWatcher = mock(IObserver.class); + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); - IObservable w = zip(WatchableExtensions.toWatchable(10, 20, 30), WatchableExtensions.toWatchable(0, 1, 2), zipr); - w.subscribe(aWatcher); + Observable w = zip(Observable.toObservable(10, 20, 30), Observable.toObservable(0, 1, 2), zipr); + w.subscribe(aObserver); - verify(aWatcher, times(1)).onError(any(Exception.class)); + verify(aObserver, times(1)).onError(any(Exception.class)); } private Func2 getDivideZipr() { @@ -696,7 +695,7 @@ private Func2 getDivideZipr() { @Override public Integer call(Integer i1, Integer i2) { - return i1/i2; + return i1 / i2; } }; @@ -779,15 +778,15 @@ private static String getStringValue(Object o) { } } - private static class TestWatchable extends AbstractIObservable { + private static class TestObservable extends Observable { - IObserver watcher; + Observer Observer; @Override - public IDisposable subscribe(IObserver watcher) { + public Subscription subscribe(Observer Observer) { // just store the variable where it can be accessed so we can manually trigger it - this.watcher = watcher; - return WatchableExtensions.noOpSubscription(); + this.Observer = Observer; + return Observable.noOpSubscription(); } } diff --git a/rxjava-core/src/main/java/org/rx/operations/WatchableExtensions.java b/rxjava-core/src/main/java/org/rx/operations/WatchableExtensions.java deleted file mode 100644 index 4bbbb25c23..0000000000 --- a/rxjava-core/src/main/java/org/rx/operations/WatchableExtensions.java +++ /dev/null @@ -1,1380 +0,0 @@ -package org.rx.operations; - -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; -import groovy.lang.Binding; -import groovy.lang.GroovyClassLoader; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.codehaus.groovy.runtime.InvokerHelper; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.rx.functions.Func1; -import org.rx.functions.Func2; -import org.rx.functions.Func3; -import org.rx.functions.Func4; -import org.rx.functions.Functions; -import org.rx.reactive.AbstractIObservable; -import org.rx.reactive.IObservable; -import org.rx.reactive.Notification; -import org.rx.reactive.IDisposable; -import org.rx.reactive.IObserver; - - -/** - * A set of methods for creating, combining, and consuming Watchables. - *

          - * Includes overloaded methods that handle the generic Object types being passed in for closures so that we can support Closure and RubyProc and other such types from dynamic languages. - *

          - * See Functions.execute for supported types. - *

          - * The documentation for this class makes use of marble diagrams. The following legend explains these diagrams: - *

          - * - * - * @see API.Next Programmer's Guide: Watchers, Watchables, and the Reactive Pattern - * @see scala.collection: Seq - * @see MSDN: Observable Class - * @see Reactive Framework Wiki: Observable Operators - */ -public class WatchableExtensions { - - /** - * A Watchable that never sends any information to a Watcher. - * - * This Watchable is useful primarily for testing purposes. - * - * @param - * the type of item emitted by the Watchable - */ - private static class NeverWatchable extends AbstractIObservable { - public IDisposable subscribe(IObserver observer) { - return new NullWatchableSubscription(); - } - } - - /** - * A disposable object that does nothing when its unsubscribe method is called. - */ - private static class NullWatchableSubscription implements IDisposable { - public void unsubscribe() { - } - } - - /** - * A Watchable that calls a Watcher's onError closure when the Watcher subscribes. - * - * @param - * the type of object returned by the Watchable - */ - private static class ThrowWatchable extends AbstractIObservable { - private Exception exception; - - public ThrowWatchable(Exception exception) { - this.exception = exception; - } - - /** - * Accepts a Watcher and calls its onError method. - * - * @param observer - * a Watcher of this Watchable - * @return a reference to the subscription - */ - public IDisposable subscribe(IObserver observer) { - observer.onError(this.exception); - return new NullWatchableSubscription(); - } - } - - /** - * Creates a Watchable that will execute the given function when a Watcher subscribes to it. - *

          - * You can create a simple Watchable from scratch by using the create method. You pass this method a closure that accepts as a parameter the map of closures that a Watcher passes to a Watchable's subscribe method. Write - * the closure you pass to create so that it behaves as a Watchable - calling the passed-in onNext, onError, and onCompleted methods appropriately. - *

          - * A well-formed Watchable must call either the Watcher's onCompleted method exactly once or its onError method exactly once. - * - * @param - * the type emitted by the Watchable sequence - * @param func - * a closure that accepts a Watcher and calls its onNext, onError, and onCompleted methods - * as appropriate, and returns a WatchableSubscription to allow - * cancelling the subscription (if applicable) - * @return a Watchable that, when a Watcher subscribes to it, will execute the given function - */ - public static IObservable create(Func1> func) { - return wrap(new OperationToWatchableFunction(func)); - } - - /** - * Creates a Watchable that will execute the given function when a Watcher subscribes to it. - *

          - * You can create a simple Watchable from scratch by using the create method. You pass this method a closure that accepts as a parameter the map of closures that a Watcher passes to a Watchable's subscribe method. Write - * the closure you pass to create so that it behaves as a Watchable - calling the passed-in onNext, onError, and onCompleted methods appropriately. - *

          - * A well-formed Watchable must call either the Watcher's onCompleted method exactly once or its onError method exactly once. - * - * @param - * the type of the observable sequence - * @param func - * a closure that accepts a Watcher and calls its onNext, onError, and onCompleted methods - * as appropriate, and returns a WatchableSubscription to allow - * cancelling the subscription (if applicable) - * @return a Watchable that, when a Watcher subscribes to it, will execute the given function - */ - public static IObservable create(final Object callback) { - return create(new Func1>() { - - @Override - public IDisposable call(IObserver t1) { - return Functions.execute(callback, t1); - } - - }); - } - - /** - * Returns a Watchable that returns no data to the Watcher and immediately invokes its onCompleted method. - *

          - * - * - * @param - * the type of item emitted by the Watchable - * @return a Watchable that returns no data to the Watcher and immediately invokes the - * Watcher's onCompleted method - */ - public static IObservable empty() { - return toWatchable(new ArrayList()); - } - - /** - * Returns a Watchable that calls onError when a Watcher subscribes to it. - *

          - * Note: Maps to Observable.Throw in Rx - throw is a reserved word in Java. - *

          - * - * - * @param exception - * the error to throw - * @param - * the type of object returned by the Watchable - * @return a Watchable object that calls onError when a Watcher subscribes - */ - public static IObservable error(Exception exception) { - return wrap(new ThrowWatchable(exception)); - } - - /** - * Filters a Watchable by discarding any of its emissions that do not meet some test. - *

          - * - * - * @param that - * the Watchable to filter - * @param predicate - * a closure that evaluates the items emitted by the source Watchable, returning true if they pass the filter - * @return a Watchable that emits only those items in the original Watchable that the filter - * evaluates as true - */ - public static IObservable filter(IObservable that, Func1 predicate) { - return new OperationFilter(that, predicate); - } - - /** - * Filters a Watchable by discarding any of its emissions that do not meet some test. - *

          - * - * - * @param that - * the Watchable to filter - * @param predicate - * a closure that evaluates the items emitted by the source Watchable, returning true if they pass the filter - * @return a Watchable that emits only those items in the original Watchable that the filter - * evaluates as true - */ - public static IObservable filter(IObservable that, final Object function) { - return filter(that, new Func1() { - - @Override - public Boolean call(T t1) { - return Functions.execute(function, t1); - - } - - }); - } - - /** - * Returns a Watchable that notifies an observer of a single value and then completes. - *

          - * To convert any object into a Watchable that emits that object, pass that object into the just method. - *

          - * This is similar to the {@link toWatchable} method, except that toWatchable will convert an iterable object into a Watchable that emits each of the items in the iterable, one at a time, while the just method would - * convert the iterable into a Watchable that emits the entire iterable as a single item. - *

          - * This value is the equivalent of Observable.Return in the Reactive Extensions library. - *

          - * - * - * @param value - * the value to pass to the Watcher's onNext method - * @param - * the type of the value - * @return a Watchable that notifies a Watcher of a single value and then completes - */ - public static IObservable just(T value) { - List list = new ArrayList(); - list.add(value); - - return toWatchable(list); - } - - /** - * Takes the last item emitted by a source Watchable and returns a Watchable that emits only - * that item as its sole emission. - *

          - * To convert a Watchable that emits a sequence of objects into one that only emits the last object in this sequence before completing, use the last method. - *

          - * - * - * @param that - * the source Watchable - * @return a Watchable that emits a single item, which is identical to the last item emitted - * by the source Watchable - */ - public static IObservable last(final IObservable that) { - return wrap(new OperationLast(that)); - } - - /** - * Applies a closure of your choosing to every notification emitted by a Watchable, and returns - * this transformation as a new Watchable sequence. - *

          - * - * - * @param sequence - * the source Watchable - * @param func - * a closure to apply to each item in the sequence emitted by the source Watchable - * @param - * the type of items emitted by the the source Watchable - * @param - * the type of items returned by map closure func - * @return a Watchable that is the result of applying the transformation function to each item - * in the sequence emitted by the source Watchable - */ - public static IObservable map(IObservable sequence, Func1 func) { - return wrap(OperationMap.map(sequence, func)); - } - - /** - * Applies a closure of your choosing to every notification emitted by a Watchable, and returns - * this transformation as a new Watchable sequence. - *

          - * - * - * @param sequence - * the source Watchable - * @param function - * a closure to apply to each item in the sequence emitted by the source Watchable - * @param - * the type of items emitted by the the source Watchable - * @param - * the type of items returned by map closure function - * @return a Watchable that is the result of applying the transformation function to each item - * in the sequence emitted by the source Watchable - */ - public static IObservable map(IObservable sequence, final Object function) { - return map(sequence, new Func1() { - - @Override - public R call(T t1) { - return Functions.execute(function, t1); - } - - }); - } - - /** - * Creates a new Watchable sequence by applying a closure that you supply to each object in the - * original Watchable sequence, where that closure is itself a Watchable that emits objects, - * and then merges the results of that closure applied to every item emitted by the original - * Watchable, emitting these merged results as its own sequence. - *

          - * - * - * @param sequence - * the source Watchable - * @param func - * a closure to apply to each item emitted by the source Watchable, generating a - * Watchable - * @param - * the type emitted by the source Watchable - * @param - * the type emitted by the Watchables emitted by func - * @return a Watchable that emits a sequence that is the result of applying the transformation - * function to each item emitted by the source Watchable and merging the results of - * the Watchables obtained from this transformation - */ - public static IObservable mapMany(IObservable sequence, Func1, T> func) { - return wrap(OperationMap.mapMany(sequence, func)); - } - - /** - * Creates a new Watchable sequence by applying a closure that you supply to each object in the - * original Watchable sequence, where that closure is itself a Watchable that emits objects, - * and then merges the results of that closure applied to every item emitted by the original - * Watchable, emitting these merged results as its own sequence. - *

          - * - * - * @param sequence - * the source Watchable - * @param function - * a closure to apply to each item emitted by the source Watchable, generating a - * Watchable - * @param - * the type emitted by the source Watchable - * @param - * the type emitted by the Watchables emitted by function - * @return a Watchable that emits a sequence that is the result of applying the transformation - * function to each item emitted by the source Watchable and merging the results of the - * Watchables obtained from this transformation - */ - public static IObservable mapMany(IObservable sequence, final Object function) { - return mapMany(sequence, new Func1() { - - @Override - public R call(T t1) { - return Functions.execute(function, t1); - } - - }); - } - - /** - * Materializes the implicit notifications of an observable sequence as explicit notification values. - *

          - * - * - * @param source - * An observable sequence of elements to project. - * @return An observable sequence whose elements are the result of materializing the notifications of the given sequence. - * @see http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx - */ - public static IObservable> materialize(final IObservable sequence) { - return OperationMaterialize.materialize(sequence); - } - - /** - * Flattens the Watchable sequences from a list of Watchables into one Watchable sequence - * without any transformation. You can combine the output of multiple Watchables so that they - * act like a single Watchable, by using the merge method. - *

          - * - * - * @param source - * a list of Watchables that emit sequences of items - * @return a Watchable that emits a sequence of elements that are the result of flattening the - * output from the source list of Watchables - * @see MSDN: Observable.Merge Method - */ - public static IObservable merge(List> source) { - return wrap(OperationMerge.merge(source)); - } - - /** - * Flattens the Watchable sequences from a series of Watchables into one Watchable sequence - * without any transformation. You can combine the output of multiple Watchables so that they - * act like a single Watchable, by using the merge method. - *

          - * - * - * @param source - * a series of Watchables that emit sequences of items - * @return a Watchable that emits a sequence of elements that are the result of flattening the - * output from the source Watchables - * @see MSDN: Observable.Merge Method - */ - public static IObservable merge(IObservable... source) { - return wrap(OperationMerge.merge(source)); - } - - /** - * Flattens the Watchable sequences emitted by a sequence of Watchables that are emitted by a - * Watchable into one Watchable sequence without any transformation. You can combine the output - * of multiple Watchables so that they act like a single Watchable, by using the merge method. - *

          - * - * - * @param source - * a Watchable that emits Watchables - * @return a Watchable that emits a sequence of elements that are the result of flattening the - * output from the Watchables emitted by the source Watchable - * @see MSDN: Observable.Merge Method - */ - public static IObservable merge(IObservable> source) { - return wrap(OperationMerge.merge(source)); - } - - /** - * Same functionality as merge except that errors received to onError will be held until all sequences have finished (onComplete/onError) before sending the error. - *

          - * Only the first onError received will be sent. - *

          - * This enables receiving all successes from merged sequences without one onError from one sequence causing all onNext calls to be prevented. - *

          - * - * - * @param source - * a list of Watchables that emit sequences of items - * @return a Watchable that emits a sequence of elements that are the result of flattening the - * output from the source list of Watchables - * @see MSDN: Observable.Merge Method - */ - public static IObservable mergeDelayError(List> source) { - return wrap(OperationMergeDelayError.mergeDelayError(source)); - } - - /** - * Same functionality as merge except that errors received to onError will be held until all sequences have finished (onComplete/onError) before sending the error. - *

          - * Only the first onError received will be sent. - *

          - * This enables receiving all successes from merged sequences without one onError from one sequence causing all onNext calls to be prevented. - *

          - * - * - * @param source - * a series of Watchables that emit sequences of items - * @return a Watchable that emits a sequence of elements that are the result of flattening the - * output from the source Watchables - * @see MSDN: Observable.Merge Method - */ - public static IObservable mergeDelayError(IObservable... source) { - return wrap(OperationMergeDelayError.mergeDelayError(source)); - } - - /** - * Same functionality as merge except that errors received to onError will be held until all sequences have finished (onComplete/onError) before sending the error. - *

          - * Only the first onError received will be sent. - *

          - * This enables receiving all successes from merged sequences without one onError from one sequence causing all onNext calls to be prevented. - *

          - * - * - * @param source - * a Watchable that emits Watchables - * @return a Watchable that emits a sequence of elements that are the result of flattening the - * output from the Watchables emitted by the source Watchable - * @see MSDN: Observable.Merge Method - */ - public static IObservable mergeDelayError(IObservable> source) { - return wrap(OperationMergeDelayError.mergeDelayError(source)); - } - - /** - * Returns a Watchable that never sends any information to a Watcher. - * - * This observable is useful primarily for testing purposes. - *

          - * - * - * @param - * the type of item (not) emitted by the Watchable - * @return a Watchable that never sends any information to a Watcher - */ - public static IObservable never() { - return wrap(new NeverWatchable()); - } - - /** - * A WatchableSubscription that does nothing. - * - * @return - */ - public static IDisposable noOpSubscription() { - return new NullWatchableSubscription(); - } - - /** - * Instruct a Watchable to pass control to another Watchable rather than calling onError if it encounters an error. - *

          - * By default, when a Watchable encounters an error that prevents it from emitting the expected item to its Watcher, the Watchable calls its Watcher's onError closure, and then quits without calling any more of its Watcher's - * closures. The onErrorResumeNext method changes this behavior. If you pass another Watchable (resumeSequence) to a Watchable's onErrorResumeNext method, if the original Watchable encounters an error, - * instead of calling its Watcher's onError closure, it will instead relinquish control to resumeSequence which will call the Watcher's onNext method if it is able to do so. In such a case, because no - * Watchable necessarily invokes onError, the Watcher may never know that an error happened. - *

          - * You can use this to prevent errors from propagating or to supply fallback data should errors be encountered. - *

          - * - * - * @param that - * the source Watchable - * @param resumeSequence - * the Watchable that will take over if the source Watchable encounters an error - * @return the source Watchable, with its behavior modified as described - */ - public static IObservable onErrorResumeNext(final IObservable that, final IObservable resumeSequence) { - return wrap(new OperationOnErrorResumeNextViaWatchable(that, resumeSequence)); - } - - /** - * Instruct a Watchable to pass control to another Watchable (the return value of a function) - * rather than calling onError if it encounters an error. - *

          - * By default, when a Watchable encounters an error that prevents it from emitting the expected item to its Watcher, the Watchable calls its Watcher's onError closure, and then quits without calling any more of its Watcher's - * closures. The onErrorResumeNext method changes this behavior. If you pass a closure that emits a Watchable (resumeFunction) to a Watchable's onErrorResumeNext method, if the original Watchable encounters - * an error, instead of calling its Watcher's onError closure, it will instead relinquish control to this new Watchable, which will call the Watcher's onNext method if it is able to do so. In such a case, because no - * Watchable necessarily invokes onError, the Watcher may never know that an error happened. - *

          - * You can use this to prevent errors from propagating or to supply fallback data should errors be encountered. - *

          - * - * - * @param that - * the source Watchable - * @param resumeFunction - * a closure that returns a Watchable that will take over if the source Watchable - * encounters an error - * @return the source Watchable, with its behavior modified as described - */ - public static IObservable onErrorResumeNext(final IObservable that, final Func1, Exception> resumeFunction) { - return wrap(new OperationOnErrorResumeNextViaFunction(that, resumeFunction)); - } - - /** - * Instruct a Watchable to emit a particular object (as returned by a closure) to its Watcher - * rather than calling onError if it encounters an error. - *

          - * By default, when a Watchable encounters an error that prevents it from emitting the expected item to its Watcher, the Watchable calls its Watcher's onError closure, and then quits without calling any more of its Watcher's - * closures. The onErrorResumeNext method changes this behavior. If you pass a function that returns another Watchable (resumeFunction) to a Watchable's onErrorResumeNext method, if the original Watchable - * encounters an error, instead of calling its Watcher's onError closure, it will instead relinquish control to this new Watchable which will call the Watcher's onNext method if it is able to do so. In such a case, - * because no Watchable necessarily invokes onError, the Watcher may never know that an error happened. - *

          - * You can use this to prevent errors from propagating or to supply fallback data should errors be encountered. - *

          - * - * - * @param that - * the source Watchable - * @param resumeFunction - * a closure that returns a Watchable that will take over if the source Watchable - * encounters an error - * @return the source Watchable, with its behavior modified as described - */ - public static IObservable onErrorResumeNext(final IObservable that, final Object resumeFunction) { - return onErrorResumeNext(that, new Func1, Exception>() { - - @Override - public IObservable call(Exception e) { - return Functions.execute(resumeFunction, e); - } - }); - } - - /** - * Instruct a Watchable to emit a particular item to its Watcher's onNext closure - * rather than calling onError if it encounters an error. - *

          - * By default, when a Watchable encounters an error that prevents it from emitting the expected item to its Watcher, the Watchable calls its Watcher's onError closure, and then quits without calling any more of its Watcher's - * closures. The onErrorReturn method changes this behavior. If you pass a closure (resumeFunction) to a Watchable's onErrorReturn method, if the original Watchable encounters an error, instead of calling - * its Watcher's onError closure, it will instead pass the return value of resumeFunction to the Watcher's onNext method. - *

          - * You can use this to prevent errors from propagating or to supply fallback data should errors be encountered. - *

          - * - * - * @param that - * the source Watchable - * @param resumeFunction - * a closure that returns a value that will be passed into a Watcher's onNext closure if the Watchable encounters an error that would - * otherwise cause it to call onError - * @return the source Watchable, with its behavior modified as described - */ - public static IObservable onErrorReturn(final IObservable that, Func1 resumeFunction) { - return wrap(new OperationOnErrorReturn(that, resumeFunction)); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the final result from the final call to your closure as its sole - * output. - *

          - * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, has an inject method that does a similar operation on lists. - *

          - * - * - * @param - * the type item emitted by the source Watchable - * @param sequence - * the source Watchable - * @param accumulator - * an accumulator closure to be invoked on each element from the sequence, whose - * result will be used in the next accumulator call (if applicable) - * - * @return a Watchable that emits a single element that is the result of accumulating the - * output from applying the accumulator to the sequence of items emitted by the source - * Watchable - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public static IObservable reduce(IObservable sequence, Func2 accumulator) { - return wrap(OperationScan.scan(sequence, accumulator).last()); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the final result from the final call to your closure as its sole - * output. - *

          - * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, has an inject method that does a similar operation on lists. - *

          - * - * - * @param - * the type item emitted by the source Watchable - * @param sequence - * the source Watchable - * @param accumulator - * an accumulator closure to be invoked on each element from the sequence, whose - * result will be used in the next accumulator call (if applicable) - * - * @return a Watchable that emits a single element that is the result of accumulating the - * output from applying the accumulator to the sequence of items emitted by the source - * Watchable - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public static IObservable reduce(final IObservable sequence, final Object accumulator) { - return reduce(sequence, new Func2() { - - @Override - public T call(T t1, T t2) { - return Functions.execute(accumulator, t1, t2); - } - - }); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the final result from the final call to your closure as its sole - * output. - *

          - * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, has an inject method that does a similar operation on lists. - *

          - * - * - * @param - * the type item emitted by the source Watchable - * @param sequence - * the source Watchable - * @param initialValue - * a seed passed into the first execution of the accumulator closure - * @param accumulator - * an accumulator closure to be invoked on each element from the sequence, whose - * result will be used in the next accumulator call (if applicable) - * - * @return a Watchable that emits a single element that is the result of accumulating the - * output from applying the accumulator to the sequence of items emitted by the source - * Watchable - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public static IObservable reduce(IObservable sequence, T initialValue, Func2 accumulator) { - return wrap(OperationScan.scan(sequence, initialValue, accumulator).last()); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the final result from the final call to your closure as its sole - * output. - *

          - * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, has an inject method that does a similar operation on lists. - *

          - * - * - * @param - * the type item emitted by the source Watchable - * @param sequence - * the source Watchable - * @param initialValue - * a seed passed into the first execution of the accumulator closure - * @param accumulator - * an accumulator closure to be invoked on each element from the sequence, whose - * result will be used in the next accumulator call (if applicable) - * @return a Watchable that emits a single element that is the result of accumulating the - * output from applying the accumulator to the sequence of items emitted by the source - * Watchable - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public static IObservable reduce(final IObservable sequence, final T initialValue, final Object accumulator) { - return reduce(sequence, initialValue, new Func2() { - - @Override - public T call(T t1, T t2) { - return Functions.execute(accumulator, t1, t2); - } - - }); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the result of each of these iterations as its own sequence. - *

          - * - * - * @param - * the type item emitted by the source Watchable - * @param sequence - * the source Watchable - * @param accumulator - * an accumulator closure to be invoked on each element from the sequence, whose - * result will be emitted and used in the next accumulator call (if applicable) - * @return a Watchable that emits a sequence of items that are the result of accumulating the - * output from the sequence emitted by the source Watchable - * @see MSDN: Observable.Scan - */ - public static IObservable scan(IObservable sequence, Func2 accumulator) { - return wrap(OperationScan.scan(sequence, accumulator)); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the result of each of these iterations as its own sequence. - *

          - * - * - * @param - * the type item emitted by the source Watchable - * @param sequence - * the source Watchable - * @param accumulator - * an accumulator closure to be invoked on each element from the sequence, whose - * result will be emitted and used in the next accumulator call (if applicable) - * @return a Watchable that emits a sequence of items that are the result of accumulating the - * output from the sequence emitted by the source Watchable - * @see MSDN: Observable.Scan - */ - public static IObservable scan(final IObservable sequence, final Object accumulator) { - return scan(sequence, new Func2() { - - @Override - public T call(T t1, T t2) { - return Functions.execute(accumulator, t1, t2); - } - - }); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the result of each of these iterations as its own sequence. - *

          - * - * - * @param - * the type item emitted by the source Watchable - * @param sequence - * the source Watchable - * @param initialValue - * the initial (seed) accumulator value - * @param accumulator - * an accumulator closure to be invoked on each element from the sequence, whose - * result will be emitted and used in the next accumulator call (if applicable) - * @return a Watchable that emits a sequence of items that are the result of accumulating the - * output from the sequence emitted by the source Watchable - * @see MSDN: Observable.Scan - */ - public static IObservable scan(IObservable sequence, T initialValue, Func2 accumulator) { - return wrap(OperationScan.scan(sequence, initialValue, accumulator)); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the result of each of these iterations as its own sequence. - *

          - * - * - * @param - * the type item emitted by the source Watchable - * @param sequence - * the source Watchable - * @param initialValue - * the initial (seed) accumulator value - * @param accumulator - * an accumulator closure to be invoked on each element from the sequence, whose - * result will be emitted and used in the next accumulator call (if applicable) - * @return a Watchable that emits a sequence of items that are the result of accumulating the - * output from the sequence emitted by the source Watchable - * @see MSDN: Observable.Scan - */ - public static IObservable scan(final IObservable sequence, final T initialValue, final Object accumulator) { - return scan(sequence, initialValue, new Func2() { - - @Override - public T call(T t1, T t2) { - return Functions.execute(accumulator, t1, t2); - } - - }); - } - - /** - * Returns a Watchable that skips the first num items emitted by the source - * Watchable. You can ignore the first num items emitted by a watchable and attend - * only to those items that come after, by modifying the watchable with the skip method. - *

          - * - * - * @param items - * the source Watchable - * @param num - * the number of items to skip - * @return a Watchable that emits the same sequence of items emitted by the source Watchable, - * except for the first num items - * @see MSDN: Observable.Skip Method - */ - public static IObservable skip(final IObservable items, int num) { - return wrap(OperationSkip.skip(items, num)); - } - - /** - * Accepts a Watchable and wraps it in another Watchable that ensures that the resulting - * Watchable is chronologically well-behaved. - *

          - * A well-behaved observable ensures onNext, onCompleted, or onError calls to its subscribers are not interleaved, onCompleted and onError are only called once respectively, and no - * onNext calls follow onCompleted and onError calls. - * - * @param observable - * the source Watchable - * @param - * the type of item emitted by the source Watchable - * @return a watchable that is a chronologically well-behaved version of the source watchable - */ - public static IObservable synchronize(IObservable observable) { - return wrap(new OperationSynchronize(observable)); - } - - /** - * Returns a Watchable that emits the first num items emitted by the source - * Watchable. - *

          - * You can choose to pay attention only to the first num values emitted by a Watchable by calling its take method. This method returns a Watchable that will call a subscribing Watcher's onNext closure a - * maximum of num times before calling onCompleted. - *

          - * - * - * @param items - * the source Watchable - * @param num - * the number of items from the start of the sequence emitted by the source - * Watchable to emit - * @return a Watchable that only emits the first num items emitted by the source - * Watchable - */ - public static IObservable take(final IObservable items, final int num) { - return wrap(OperationTake.take(items, num)); - } - - /** - * Returns a Watchable that emits a single item, a list composed of all the items emitted by - * the source Watchable. - *

          - * Normally, a Watchable that returns multiple items will do so by calling its Watcher's onNext closure for each such item. You can change this behavior, instructing the Watchable to compose a list of all of these multiple items and - * then to call the Watcher's onNext closure once, passing it the entire list, by calling the Watchable object's toList method prior to calling its subscribe method. - *

          - * - * - * @param that - * the source Watchable - * @return a Watchable that emits a single item: a List containing all of the - * items emitted by the source Watchable - */ - public static IObservable> toList(final IObservable that) { - return wrap(new OperationToWatchableList(that)); - } - - /** - * Sort T objects by their natural order (object must implement Comparable). - *

          - * - * - * @param sequence - * @throws ClassCastException - * if T objects do not implement Comparable - * @return - */ - public static IObservable> toSortedList(IObservable sequence) { - return wrap(OperationToWatchableSortedList.toSortedList(sequence)); - } - - /** - * Sort T objects using the defined sort function. - *

          - * - * - * @param sequence - * @param sortFunction - * @return - */ - public static IObservable> toSortedList(IObservable sequence, Func2 sortFunction) { - return wrap(OperationToWatchableSortedList.toSortedList(sequence, sortFunction)); - } - - /** - * Sort T objects using the defined sort function. - *

          - * - * - * @param sequence - * @param sortFunction - * @return - */ - public static IObservable> toSortedList(IObservable sequence, final Object sortFunction) { - return wrap(OperationToWatchableSortedList.toSortedList(sequence, new Func2() { - - @Override - public Integer call(T t1, T t2) { - return Functions.execute(sortFunction, t1, t2); - } - - })); - } - - /** - * Converts an Iterable sequence to a Watchable sequence. - * - * Any object that supports the Iterable interface can be converted into a Watchable that emits - * each iterable item in the object, by passing the object into the toWatchable method. - *

          - * - * - * @param iterable - * the source Iterable sequence - * @param - * the type of items in the iterable sequence and the type emitted by the resulting - * Watchable - * @return a Watchable that emits each item in the source Iterable sequence - */ - public static IObservable toWatchable(Iterable iterable) { - return wrap(new OperationToWatchableIterable(iterable)); - } - - /** - * Converts an Array sequence to a Watchable sequence. - * - * An Array can be converted into a Watchable that emits each item in the Array, by passing the - * Array into the toWatchable method. - *

          - * - * - * @param iterable - * the source Array - * @param - * the type of items in the Array, and the type of items emitted by the resulting - * Watchable - * @return a Watchable that emits each item in the source Array - */ - public static IObservable toWatchable(T... items) { - return toWatchable(Arrays.asList(items)); - } - - /** - * Allow wrapping responses with the AbstractIObservable so that we have all of - * the utility methods available for subscribing. - *

          - * This is not expected to benefit Java usage, but is intended for dynamic script which are a primary target of the Observable operations. - *

          - * Since they are dynamic they can execute the "hidden" methods on AbstractIObservable while appearing to only receive an IObservable without first casting. - * - * @param o - * @return - */ - private static AbstractIObservable wrap(final IObservable o) { - if (o instanceof AbstractIObservable) { - // if the Watchable is already an AbstractWatchable, don't wrap it again. - return (AbstractIObservable) o; - } - return new AbstractIObservable() { - - @Override - public IDisposable subscribe(IObserver observer) { - return o.subscribe(observer); - } - - }; - } - - /** - * Returns a Watchable that applies a closure of your choosing to the combination of items - * emitted, in sequence, by two other Watchables, with the results of this closure becoming the - * sequence emitted by the returned Watchable. - *

          - * zip applies this closure in strict sequence, so the first item emitted by the new Watchable will be the result of the closure applied to the first item emitted by w0 and the first item emitted by w1; the - * second item emitted by the new Watchable will be the result of the closure applied to the second item emitted by w0 and the second item emitted by w1; and so forth. - *

          - * The resulting Watchable returned from zip will call onNext as many times as the number onNext calls of the source Watchable with the shortest sequence. - *

          - * - * - * @param w0 - * one source Watchable - * @param w1 - * another source Watchable - * @param reduceFunction - * a closure that, when applied to an item emitted by each of the source Watchables, - * results in a value that will be emitted by the resulting Watchable - * @return a Watchable that emits the zipped results - */ - public static IObservable zip(IObservable w0, IObservable w1, Func2 reduceFunction) { - return wrap(OperationZip.zip(w0, w1, reduceFunction)); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the combination of items - * emitted, in sequence, by two other Watchables, with the results of this closure becoming the - * sequence emitted by the returned Watchable. - *

          - * zip applies this closure in strict sequence, so the first item emitted by the new Watchable will be the result of the closure applied to the first item emitted by w0 and the first item emitted by w1; the - * second item emitted by the new Watchable will be the result of the closure applied to the second item emitted by w0 and the second item emitted by w1; and so forth. - *

          - * The resulting Watchable returned from zip will call onNext as many times as the number onNext calls of the source Watchable with the shortest sequence. - *

          - * - * - * @param w0 - * one source Watchable - * @param w1 - * another source Watchable - * @param reduceFunction - * a closure that, when applied to an item emitted by each of the source Watchables, - * results in a value that will be emitted by the resulting Watchable - * @return a Watchable that emits the zipped results - */ - public static IObservable zip(IObservable w0, IObservable w1, final Object function) { - return zip(w0, w1, new Func2() { - - @Override - public R call(T0 t0, T1 t1) { - return Functions.execute(function, t0, t1); - } - - }); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the combination of items - * emitted, in sequence, by three other Watchables, with the results of this closure becoming - * the sequence emitted by the returned Watchable. - *

          - * zip applies this closure in strict sequence, so the first item emitted by the new Watchable will be the result of the closure applied to the first item emitted by w0, the first item emitted by w1, and the - * first item emitted by w2; the second item emitted by the new Watchable will be the result of the closure applied to the second item emitted by w0, the second item emitted by w1, and the second item - * emitted by w2; and so forth. - *

          - * The resulting Watchable returned from zip will call onNext as many times as the number onNext calls of the source Watchable with the shortest sequence. - *

          - * - * - * @param w0 - * one source Watchable - * @param w1 - * another source Watchable - * @param w2 - * a third source Watchable - * @param function - * a closure that, when applied to an item emitted by each of the source Watchables, - * results in a value that will be emitted by the resulting Watchable - * @return a Watchable that emits the zipped results - */ - public static IObservable zip(IObservable w0, IObservable w1, IObservable w2, Func3 function) { - return wrap(OperationZip.zip(w0, w1, w2, function)); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the combination of items - * emitted, in sequence, by three other Watchables, with the results of this closure becoming - * the sequence emitted by the returned Watchable. - *

          - * zip applies this closure in strict sequence, so the first item emitted by the new Watchable will be the result of the closure applied to the first item emitted by w0, the first item emitted by w1, and the - * first item emitted by w2; the second item emitted by the new Watchable will be the result of the closure applied to the second item emitted by w0, the second item emitted by w1, and the second item - * emitted by w2; and so forth. - *

          - * The resulting Watchable returned from zip will call onNext as many times as the number onNext calls of the source Watchable with the shortest sequence. - *

          - * - * - * @param w0 - * one source Watchable - * @param w1 - * another source Watchable - * @param w2 - * a third source Watchable - * @param function - * a closure that, when applied to an item emitted by each of the source Watchables, - * results in a value that will be emitted by the resulting Watchable - * @return a Watchable that emits the zipped results - */ - public static IObservable zip(IObservable w0, IObservable w1, IObservable w2, final Object function) { - return zip(w0, w1, w2, new Func3() { - - @Override - public R call(T0 t0, T1 t1, T2 t2) { - return Functions.execute(function, t0, t1, t2); - } - - }); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the combination of items - * emitted, in sequence, by four other Watchables, with the results of this closure becoming - * the sequence emitted by the returned Watchable. - *

          - * zip applies this closure in strict sequence, so the first item emitted by the new Watchable will be the result of the closure applied to the first item emitted by w0, the first item emitted by w1, the - * first item emitted by w2, and the first item emitted by w3; the second item emitted by the new Watchable will be the result of the closure applied to the second item emitted by each of those Watchables; and so forth. - *

          - * The resulting Watchable returned from zip will call onNext as many times as the number onNext calls of the source Watchable with the shortest sequence. - *

          - * - * - * @param w0 - * one source Watchable - * @param w1 - * another source Watchable - * @param w2 - * a third source Watchable - * @param w3 - * a fourth source Watchable - * @param reduceFunction - * a closure that, when applied to an item emitted by each of the source Watchables, - * results in a value that will be emitted by the resulting Watchable - * @return a Watchable that emits the zipped results - */ - public static IObservable zip(IObservable w0, IObservable w1, IObservable w2, IObservable w3, Func4 reduceFunction) { - return wrap(OperationZip.zip(w0, w1, w2, w3, reduceFunction)); - } - - /** - * Returns a Watchable that applies a closure of your choosing to the combination of items - * emitted, in sequence, by four other Watchables, with the results of this closure becoming - * the sequence emitted by the returned Watchable. - *

          - * zip applies this closure in strict sequence, so the first item emitted by the new Watchable will be the result of the closure applied to the first item emitted by w0, the first item emitted by w1, the - * first item emitted by w2, and the first item emitted by w3; the second item emitted by the new Watchable will be the result of the closure applied to the second item emitted by each of those Watchables; and so forth. - *

          - * The resulting Watchable returned from zip will call onNext as many times as the number onNext calls of the source Watchable with the shortest sequence. - *

          - * - * - * @param w0 - * one source Watchable - * @param w1 - * another source Watchable - * @param w2 - * a third source Watchable - * @param w3 - * a fourth source Watchable - * @param function - * a closure that, when applied to an item emitted by each of the source Watchables, - * results in a value that will be emitted by the resulting Watchable - * @return a Watchable that emits the zipped results - */ - public static IObservable zip(IObservable w0, IObservable w1, IObservable w2, IObservable w3, final Object function) { - return zip(w0, w1, w2, w3, new Func4() { - - @Override - public R call(T0 t0, T1 t1, T2 t2, T3 t3) { - return Functions.execute(function, t0, t1, t2, t3); - } - - }); - } - - public static class UnitTest { - @Mock - ScriptAssertion assertion; - - @Mock - IObserver w; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testReduce() { - IObservable watchable = toWatchable(1, 2, 3, 4); - reduce(watchable, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t1 + t2; - } - - }).subscribe(w); - // we should be called only once - verify(w, times(1)).onNext(anyInt()); - verify(w).onNext(10); - } - - @Test - public void testReduceWithInitialValue() { - IObservable watchable = toWatchable(1, 2, 3, 4); - reduce(watchable, 50, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t1 + t2; - } - - }).subscribe(w); - // we should be called only once - verify(w, times(1)).onNext(anyInt()); - verify(w).onNext(60); - } - - @Test - public void testCreateViaGroovy() { - runGroovyScript("o.create({it.onNext('hello');it.onCompleted();}).subscribe({ result -> a.received(result)});"); - verify(assertion, times(1)).received("hello"); - } - - @Test - public void testFilterViaGroovy() { - runGroovyScript("o.filter(o.toWatchable(1, 2, 3), {it >= 2}).subscribe({ result -> a.received(result)});"); - verify(assertion, times(0)).received(1); - verify(assertion, times(1)).received(2); - verify(assertion, times(1)).received(3); - } - - @Test - public void testMapViaGroovy() { - runGroovyScript("o.map(o.toWatchable(1, 2, 3), {'hello_' + it}).subscribe({ result -> a.received(result)});"); - verify(assertion, times(1)).received("hello_" + 1); - verify(assertion, times(1)).received("hello_" + 2); - verify(assertion, times(1)).received("hello_" + 3); - } - - @Test - public void testSkipViaGroovy() { - runGroovyScript("o.skip(o.toWatchable(1, 2, 3), 2).subscribe({ result -> a.received(result)});"); - verify(assertion, times(0)).received(1); - verify(assertion, times(0)).received(2); - verify(assertion, times(1)).received(3); - } - - @Test - public void testTakeViaGroovy() { - runGroovyScript("o.take(o.toWatchable(1, 2, 3), 2).subscribe({ result -> a.received(result)});"); - verify(assertion, times(1)).received(1); - verify(assertion, times(1)).received(2); - verify(assertion, times(0)).received(3); - } - - @Test - public void testSkipTakeViaGroovy() { - runGroovyScript("o.skip(o.toWatchable(1, 2, 3), 1).take(1).subscribe({ result -> a.received(result)});"); - verify(assertion, times(0)).received(1); - verify(assertion, times(1)).received(2); - verify(assertion, times(0)).received(3); - } - - @Test - public void testMergeDelayErrorViaGroovy() { - runGroovyScript("o.mergeDelayError(o.toWatchable(1, 2, 3), o.merge(o.toWatchable(6), o.error(new NullPointerException()), o.toWatchable(7)), o.toWatchable(4, 5)).subscribe({ result -> a.received(result)}, { exception -> a.error(exception)});"); - verify(assertion, times(1)).received(1); - verify(assertion, times(1)).received(2); - verify(assertion, times(1)).received(3); - verify(assertion, times(1)).received(4); - verify(assertion, times(1)).received(5); - verify(assertion, times(1)).received(6); - verify(assertion, times(0)).received(7); - verify(assertion, times(1)).error(any(NullPointerException.class)); - } - - @Test - public void testMergeViaGroovy() { - runGroovyScript("o.merge(o.toWatchable(1, 2, 3), o.merge(o.toWatchable(6), o.error(new NullPointerException()), o.toWatchable(7)), o.toWatchable(4, 5)).subscribe({ result -> a.received(result)}, { exception -> a.error(exception)});"); - // executing synchronously so we can deterministically know what order things will come - verify(assertion, times(1)).received(1); - verify(assertion, times(1)).received(2); - verify(assertion, times(1)).received(3); - verify(assertion, times(0)).received(4); // the NPE will cause this sequence to be skipped - verify(assertion, times(0)).received(5); // the NPE will cause this sequence to be skipped - verify(assertion, times(1)).received(6); // this comes before the NPE so should exist - verify(assertion, times(0)).received(7);// this comes in the sequence after the NPE - verify(assertion, times(1)).error(any(NullPointerException.class)); - } - - @Test - public void testMaterializeViaGroovy() { - runGroovyScript("o.materialize(o.toWatchable(1, 2, 3)).subscribe({ result -> a.received(result)});"); - // we expect 4 onNext calls: 3 for 1, 2, 3 WatchableNotification.OnNext and 1 for WatchableNotification.OnCompleted - verify(assertion, times(4)).received(any(Notification.class)); - verify(assertion, times(0)).error(any(Exception.class)); - } - - @Test - public void testToSortedList() { - runGroovyScript("o.toSortedList(o.toWatchable(1, 3, 2, 5, 4)).subscribe({ result -> a.received(result)});"); - verify(assertion, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); - } - - @Test - public void testToSortedListWithFunction() { - runGroovyScript("o.toSortedList(o.toWatchable(1, 3, 2, 5, 4), {a, b -> a - b}).subscribe({ result -> a.received(result)});"); - verify(assertion, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); - } - - private void runGroovyScript(String script) { - ClassLoader parent = getClass().getClassLoader(); - GroovyClassLoader loader = new GroovyClassLoader(parent); - - Binding binding = new Binding(); - binding.setVariable("a", assertion); - binding.setVariable("o", org.rx.operations.WatchableExtensions.class); - - /* parse the script and execute it */ - InvokerHelper.createScript(loader.parseClass(script), binding).run(); - } - - private static interface ScriptAssertion { - public void received(Object o); - - public void error(Exception o); - } - } -} diff --git a/rxjava-core/src/main/java/org/rx/operations/package.html b/rxjava-core/src/main/java/org/rx/operations/package.html index 4e0a60aec8..fbb0cfa3be 100644 --- a/rxjava-core/src/main/java/org/rx/operations/package.html +++ b/rxjava-core/src/main/java/org/rx/operations/package.html @@ -1,6 +1,6 @@ -

          Operators that allow composing Watchables to transform and +

          Operators that allow composing Observables to transform and manipulate data in an asynchronous, functional and thread-safe manner.

          -

          The operators are all exposed via the WatchableExtensions class

          +

          The operators are all exposed via the ObservableExtensions class

          \ No newline at end of file diff --git a/rxjava-core/src/main/java/org/rx/reactive/AbstractIObservable.java b/rxjava-core/src/main/java/org/rx/reactive/AbstractIObservable.java deleted file mode 100644 index f7579e946c..0000000000 --- a/rxjava-core/src/main/java/org/rx/reactive/AbstractIObservable.java +++ /dev/null @@ -1,494 +0,0 @@ -package org.rx.reactive; - -import static org.mockito.Mockito.*; -import groovy.lang.Binding; -import groovy.lang.GroovyClassLoader; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.codehaus.groovy.runtime.InvokerHelper; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.rx.functions.Func1; -import org.rx.functions.Func2; -import org.rx.functions.Functions; -import org.rx.operations.OperationMaterialize; -import org.rx.operations.WatchableExtensions; - - -/** - * Abstract parent class to provide common functionality of classes implementing the Watchable interface. - *

          - * It provides overloaded methods for subscribing as well as delegate methods to the WatchableExtensions. - * - * @param - * - * @ExcludeFromSDKJavadoc - */ -public abstract class AbstractIObservable implements IObservable { - private static volatile APIWatchableErrorHandler errorHandler; - - /** - * An implementation of subscribe must be provided to comply with the IObservable interface to allow subscribing for push notifications. - * - * @see IObservable - */ - public abstract IDisposable subscribe(IObserver observer); - - /** - * Register a single global error handler which will be given all Exceptions that are passed through an onError method call. - * - * @param handler - */ - public static void registerErrorHandler(APIWatchableErrorHandler handler) { - errorHandler = handler; - } - - /** - * When an error occurs in any Watcher we will invoke this to allow it to be handled by the global APIWatchableErrorHandler - * - * @param e - */ - private static void handleError(Exception e) { - if (errorHandler != null) { - try { - errorHandler.handleError(e); - } catch (Exception ex) { - // TODO replace this with something other than logger. - // logger.error("Failed to send exception to APIWatchableErrorHandler.", ex); - } - } - } - - /** - * @ExcludeFromSDKJavadoc - */ - public static interface APIWatchableErrorHandler { - public void handleError(Exception e); - } - - /** - * Execute the callback with the given arguments. - *

          - * The callbacks align with the onCompleted, onError and onNext methods an an IObserver. - * - * @param callback - * Object to be invoked. It is left to the implementing class to determine the type, such as a Groovy Closure or JRuby closure conversion. - * @param args - */ - protected void executeCallback(final Object callback, Object... args) { - Functions.execute(callback, args); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public IDisposable subscribe(final Object onNext, final Object onError, final Object onComplete) { - return subscribe(new IObserver() { - - @Override - public void onCompleted() { - if (onComplete != null) { - executeCallback(onComplete); - } - } - - @Override - public void onError(Exception e) { - handleError(e); - if (onError != null) { - executeCallback(onError, e); - } - } - - @Override - public void onNext(Object args) { - if (onNext == null) { - throw new RuntimeException("onNext must be implemented"); - } - executeCallback(onNext, args); - } - - }); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public IDisposable subscribe(final Object onNext, final Object onError) { - return subscribe(new IObserver() { - - @Override - public void onCompleted() { - // do nothing - } - - @Override - public void onError(Exception e) { - handleError(e); - if (onError != null) { - executeCallback(onError, e); - } - } - - @Override - public void onNext(Object args) { - if (onNext == null) { - throw new RuntimeException("onNext must be implemented"); - } - executeCallback(onNext, args); - } - - }); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public IDisposable subscribe(final Object onNext) { - if (onNext instanceof IObserver) { - throw new RuntimeException("Watchers are not intended to be passed to this generic method. Your generic type is most likely wrong. This method is for dynamic code to send in closures."); - } - return subscribe(new IObserver() { - - @Override - public void onCompleted() { - // do nothing - } - - @Override - public void onError(Exception e) { - handleError(e); - // no callback defined - } - - @Override - public void onNext(Object args) { - if (onNext == null) { - throw new RuntimeException("onNext must be implemented"); - } - executeCallback(onNext, args); - } - - }); - } - - /** - * Helper method which acts as a synonym to execute(APIServiceCallback callback) so that groovy can pass in a closure without the as com.netflix.api.service.APIServiceCallback at the end of it. - * - * @param callbacks - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public IDisposable subscribe(final Map callbacks) { - return subscribe(new IObserver() { - - @Override - public void onCompleted() { - Object completed = callbacks.get("onCompleted"); - if (completed != null) { - executeCallback(completed); - } - } - - @Override - public void onError(Exception e) { - handleError(e); - Object onError = callbacks.get("onError"); - if (onError != null) { - executeCallback(onError, e); - } - } - - @Override - public void onNext(Object args) { - Object onNext = callbacks.get("onNext"); - if (onNext == null) { - throw new RuntimeException("onNext must be implemented"); - } - executeCallback(onNext, args); - } - - }); - } - - @Override - public IObservable mapMany(final Object callback) { - return WatchableExtensions.mapMany(this, new Func1, T>() { - - @Override - public IObservable call(T t1) { - return Functions.execute(callback, t1); - } - }); - } - - @Override - public IObservable mapMany(Func1, T> func) { - return WatchableExtensions.mapMany(this, func); - } - - @Override - public IObservable map(final Object callback) { - return WatchableExtensions.map(this, new Func1() { - - @Override - public R call(T t1) { - return Functions.execute(callback, t1); - } - }); - } - - @Override - public IObservable map(Func1 func) { - return WatchableExtensions.map(this, func); - } - - @Override - public IObservable> toList() { - return WatchableExtensions.toList(this); - } - - @Override - public IObservable> toSortedList() { - return WatchableExtensions.toSortedList(this); - } - - @Override - public IObservable> toSortedList(Func2 sortFunction) { - return WatchableExtensions.toSortedList(this, sortFunction); - } - - @Override - public IObservable> toSortedList(final Object sortFunction) { - return WatchableExtensions.toSortedList(this, sortFunction); - } - - @Override - public IObservable take(final int num) { - return WatchableExtensions.take(this, num); - } - - @Override - public IObservable filter(final Object callback) { - return WatchableExtensions.filter(this, new Func1() { - - @Override - public Boolean call(T t1) { - return Functions.execute(callback, t1); - } - }); - } - - @Override - public IObservable filter(Func1 predicate) { - return WatchableExtensions.filter(this, predicate); - } - - @Override - public IObservable last() { - return WatchableExtensions.last(this); - } - - @Override - public IObservable onErrorResumeNext(final IObservable resumeSequence) { - return WatchableExtensions.onErrorResumeNext(this, resumeSequence); - } - - @Override - public IObservable onErrorResumeNext(final Func1, Exception> resumeFunction) { - return WatchableExtensions.onErrorResumeNext(this, resumeFunction); - } - - @Override - public IObservable onErrorResumeNext(final Object resumeFunction) { - return WatchableExtensions.onErrorResumeNext(this, new Func1, Exception>() { - - @Override - public IObservable call(Exception e) { - return Functions.execute(resumeFunction, e); - } - }); - } - - @Override - public IObservable onErrorReturn(final Object resumeFunction) { - return WatchableExtensions.onErrorReturn(this, new Func1() { - - @Override - public T call(Exception e) { - return Functions.execute(resumeFunction, e); - } - }); - } - - @Override - public IObservable onErrorReturn(Func1 resumeFunction) { - return WatchableExtensions.onErrorReturn(this, resumeFunction); - } - - @Override - public IObservable reduce(Func2 accumulator) { - return WatchableExtensions.reduce(this, accumulator); - } - - @Override - public IObservable reduce(Object accumulator) { - return WatchableExtensions.reduce(this, accumulator); - } - - @Override - public IObservable reduce(T initialValue, Func2 accumulator) { - return WatchableExtensions.reduce(this, initialValue, accumulator); - } - - @Override - public IObservable reduce(T initialValue, Object accumulator) { - return WatchableExtensions.reduce(this, initialValue, accumulator); - } - - @Override - public IObservable skip(int num) { - return WatchableExtensions.skip(this, num); - } - - @Override - public IObservable scan(Func2 accumulator) { - return WatchableExtensions.scan(this, accumulator); - } - - @Override - public IObservable scan(final Object accumulator) { - return WatchableExtensions.scan(this, accumulator); - } - - @Override - public IObservable scan(T initialValue, Func2 accumulator) { - return WatchableExtensions.scan(this, initialValue, accumulator); - } - - @Override - public IObservable scan(final T initialValue, final Object accumulator) { - return WatchableExtensions.scan(this, initialValue, accumulator); - } - - @Override - public IObservable> materialize() { - return OperationMaterialize.materialize(this); - } - - public static class UnitTest { - @Mock - ScriptAssertion assertion; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testLast() { - String script = "mockApiCall.getObservable().last().subscribe({ result -> a.received(result)});"; - runGroovyScript(script); - verify(assertion, times(1)).received("hello_1"); - } - - @Test - public void testMap() { - String script = "mockApiCall.getObservable().map({v -> 'say' + v}).subscribe({ result -> a.received(result)});"; - runGroovyScript(script); - verify(assertion, times(1)).received("sayhello_1"); - } - - @Test - public void testScriptWithOnNext() { - String script = "mockApiCall.getObservable().subscribe({ result -> a.received(result)})"; - runGroovyScript(script); - verify(assertion).received("hello_1"); - } - - @Test - public void testScriptWithMerge() { - String script = "o.merge(mockApiCall.getObservable(), mockApiCall.getObservable()).subscribe({ result -> a.received(result)});"; - runGroovyScript(script); - verify(assertion, times(1)).received("hello_1"); - verify(assertion, times(1)).received("hello_2"); - } - - @Test - public void testScriptWithMaterialize() { - String script = "mockApiCall.getObservable().materialize().subscribe({ result -> a.received(result)});"; - runGroovyScript(script); - // 2 times: once for hello_1 and once for onCompleted - verify(assertion, times(2)).received(any(Notification.class)); - } - - @Test - public void testToSortedList() { - runGroovyScript("mockApiCall.getNumbers().toSortedList().subscribe({ result -> a.received(result)});"); - verify(assertion, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); - } - - @Test - public void testToSortedListWithFunction() { - runGroovyScript("mockApiCall.getNumbers().toSortedList({a, b -> a - b}).subscribe({ result -> a.received(result)});"); - verify(assertion, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); - } - - private void runGroovyScript(String script) { - ClassLoader parent = getClass().getClassLoader(); - GroovyClassLoader loader = new GroovyClassLoader(parent); - - Binding binding = new Binding(); - binding.setVariable("mockApiCall", new TestFactory()); - binding.setVariable("a", assertion); - binding.setVariable("o", org.rx.operations.WatchableExtensions.class); - - /* parse the script and execute it */ - InvokerHelper.createScript(loader.parseClass(script), binding).run(); - } - - private static class TestFactory { - int counter = 1; - - @SuppressWarnings("unused") - public TestObservable getObservable() { - return new TestObservable(counter++); - } - - @SuppressWarnings("unused") - public IObservable getNumbers() { - return WatchableExtensions.toWatchable(1, 3, 2, 5, 4); - } - } - - private static class TestObservable extends AbstractIObservable { - private final int count; - - public TestObservable(int count) { - this.count = count; - } - - @Override - public IDisposable subscribe(IObserver observer) { - - observer.onNext("hello_" + count); - observer.onCompleted(); - - return new IDisposable() { - - @Override - public void unsubscribe() { - // unregister ... will never be called here since we are executing synchronously - } - - }; - } - } - - private static interface ScriptAssertion { - public void received(Object o); - } - } -} diff --git a/rxjava-core/src/main/java/org/rx/reactive/IObservable.java b/rxjava-core/src/main/java/org/rx/reactive/IObservable.java deleted file mode 100644 index 22b6ea716b..0000000000 --- a/rxjava-core/src/main/java/org/rx/reactive/IObservable.java +++ /dev/null @@ -1,557 +0,0 @@ -package org.rx.reactive; - -import java.util.List; -import java.util.Map; - -import org.rx.functions.Func1; -import org.rx.functions.Func2; - - -/** - * The Watchable interface that implements the Reactive Pattern. - * The documentation for this interface makes use of marble diagrams. The following legend explains - * these diagrams: - *

          - * - */ - -public interface IObservable { - - /** - * A Watcher must call a Watchable's subscribe method in order to register itself - * to receive push-based notifications from the Watchable. A typical implementation of the - * subscribe method does the following: - * - * It stores a reference to the Watcher in a collection object, such as a List - * object. - * - * It returns a reference to an WatchableSubscription interface. This enables - * Watchers to unsubscribe (that is, to stop receiving notifications) before the Watchable has - * finished sending them and has called the Watcher's OnCompleted method. - * - * At any given time, a particular instance of an Watchable implementation is - * responsible for accepting all subscriptions and notifying all subscribers. Unless the - * documentation for a particular Watchable implementation indicates otherwise, - * Watchers should make no assumptions about the Watchable implementation, such - * as the order of notifications that multiple Watchers will receive. - *

          - * For more information see - * API.Next Programmer's Guide: Watchers, Watchables, and the Reactive Pattern - * - * @param watcher - * @return a WatchableSubscription reference to an interface that allows observers - * to stop receiving notifications before the provider has finished sending them - */ - public IDisposable subscribe(IObserver watcher); - - /** - * Filters a Watchable by discarding any of its emissions that do not meet some test. - *

          - * - * - * @param callback - * a closure that evaluates the items emitted by the source watchable, returning - * true if they pass the filter - * @return a watchable that emits only those items in the original watchable that the filter - * evaluates as "true" - */ - public IObservable filter(Object callback); - - /** - * Filters a Watchable by discarding any of its emissions that do not meet some test. - *

          - * - * - * @param predicate - * a closure that evaluates the items emitted by the source watchable, returning - * true if they pass the filter - * @return a watchable that emits only those items in the original watchable that the filter - * evaluates as true - */ - public IObservable filter(Func1 predicate); - - /** - * Converts a Watchable that emits a sequence of objects into one that only emits the last - * object in this sequence before completing. - *

          - * - * - * @return a Watchable that emits only the last item emitted by the original watchable - */ - public IObservable last(); - - /** - * Applies a closure of your choosing to every item emitted by a Watchable, and returns this - * transformation as a new Watchable sequence. - *

          - * - * - * @param callback - * a closure to apply to each item in the sequence. - * @return a Watchable that emits a sequence that is the result of applying the transformation - * closure to each item in the sequence emitted by the input Watchable. - */ - public IObservable map(Object callback); - - /** - * Applies a closure of your choosing to every item emitted by a Watchable, and returns this - * transformation as a new Watchable sequence. - *

          - * - * - * @param func - * a closure to apply to each item in the sequence. - * @return a Watchable that emits a sequence that is the result of applying the transformation - * closure to each item in the sequence emitted by the input Watchable. - */ - public IObservable map(Func1 func); - - /** - * Creates a new Watchable sequence by applying a closure that you supply to each item in the - * original Watchable sequence, where that closure is itself a Watchable that emits items, and - * then merges the results of that closure applied to every item emitted by the original - * Watchable, emitting these merged results as its own sequence. - *

          - * - * - * @param callback - * a closure to apply to each item in the sequence that returns a Watchable. - * @return a Watchable that emits a sequence that is the result of applying the transformation' - * function to each item in the input sequence and merging the results of the - * Watchables obtained from this transformation. - */ - public IObservable mapMany(Object callback); - - /** - * Creates a new Watchable sequence by applying a closure that you supply to each item in the - * original Watchable sequence, where that closure is itself a Watchable that emits items, and - * then merges the results of that closure applied to every item emitted by the original - * Watchable, emitting these merged results as its own sequence. - *

          - * - * - * @param func - * a closure to apply to each item in the sequence, that returns a Watchable. - * @return a Watchable that emits a sequence that is the result of applying the transformation - * function to each item in the input sequence and merging the results of the - * Watchables obtained from this transformation. - */ - public IObservable mapMany(Func1, T> func); - - /** - * Materializes the implicit notifications of this observable sequence as explicit notification values. - *

          - * - * - * @return An observable sequence whose elements are the result of materializing the notifications of the given sequence. - * @see http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx - */ - public IObservable> materialize(); - - /** - * Instruct a Watchable to pass control to another Watchable rather than calling - * onError if it encounters an error. - * - * By default, when a Watchable encounters an error that prevents it from emitting the expected - * item to its Watcher, the Watchable calls its Watcher's onError closure, and - * then quits without calling any more of its Watcher's closures. The - * onErrorResumeNext method changes this behavior. If you pass another watchable - * (resumeSequence) to a Watchable's onErrorResumeNext method, if the - * original Watchable encounters an error, instead of calling its Watcher's - * onError closure, it will instead relinquish control to - * resumeSequence which will call the Watcher's onNext method if it - * is able to do so. In such a case, because no Watchable necessarily invokes - * onError, the Watcher may never know that an error happened. - * - * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - *

          - * - * @param resumeSequence - * @return the original watchable, with appropriately modified behavior - */ - public IObservable onErrorResumeNext(IObservable resumeSequence); - - /** - * Instruct a Watchable to pass control to another Watchable rather than calling onError if it encounters an error. - * - * By default, when a Watchable encounters an error that prevents it from emitting the expected - * item to its Watcher, the Watchable calls its Watcher's onError closure, and - * then quits without calling any more of its Watcher's closures. The - * onErrorResumeNext method changes this behavior. If you pass another watchable - * (resumeFunction) to a Watchable's onErrorResumeNext method, if the - * original Watchable encounters an error, instead of calling its Watcher's - * onErrort closure, it will instead relinquish control to - * resumeFunction which will call the Watcher's onNext method if it - * is able to do so. In such a case, because no Watchable necessarily invokes - * onError, the Watcher may never know that an error happened. - * - * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - *

          - * - * @param resumeFunction - * @return the original watchable, with appropriately modified behavior - */ - public IObservable onErrorResumeNext(Func1, Exception> resumeFunction); - - /** - * Instruct a Watchable to emit a particular item rather than calling onError if - * it encounters an error. - * - * By default, when a Watchable encounters an error that prevents it from emitting the expected - * item to its Watcher, the Watchable calls its Watcher's onError closure, and - * then quits without calling any more of its Watcher's closures. The - * onErrorResumeNext method changes this behavior. If you pass another watchable - * (resumeFunction) to a Watchable's onErrorResumeNext method, if the - * original Watchable encounters an error, instead of calling its Watcher's - * onError closure, it will instead relinquish control to - * resumeFunction which will call the Watcher's onNext method if it - * is able to do so. In such a case, because no Watchable necessarily invokes - * onError, the Watcher may never know that an error happened. - * - * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - *

          - * - * @param resumeFunction - * @return the original watchable with appropriately modified behavior - */ - public IObservable onErrorResumeNext(Object resumeFunction); - - /** - * Instruct a Watchable to emit a particular item rather than calling onError if - * it encounters an error. - * - * By default, when a Watchable encounters an error that prevents it from emitting the expected - * object to its Watcher, the Watchable calls its Watcher's onError closure, and - * then quits without calling any more of its Watcher's closures. The - * onErrorReturn method changes this behavior. If you pass a closure - * (resumeFunction) to a Watchable's onErrorReturn method, if the - * original Watchable encounters an error, instead of calling its Watcher's - * onError closure, it will instead call pass the return value of - * resumeFunction to the Watcher's onNext method. - * - * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - *

          - * - * - * @param resumeFunction - * @return the original watchable with appropriately modified behavior - */ - public IObservable onErrorReturn(Func1 resumeFunction); - - /** - * Instruct a Watchable to emit a particular item rather than calling onError if - * it encounters an error. - * - * By default, when a Watchable encounters an error that prevents it from emitting the expected - * object to its Watcher, the Watchable calls its Watcher's onError closure, and - * then quits without calling any more of its Watcher's closures. The - * onErrorReturn method changes this behavior. If you pass a closure - * (resumeFunction) to a Watchable's onErrorReturn method, if the - * original Watchable encounters an error, instead of calling its Watcher's - * onError closure, it will instead call pass the return value of - * resumeFunction to the Watcher's onNext method. - * - * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - *

          - * - * - * @param that - * @param resumeFunction - * @return the original watchable with appropriately modified behavior - */ - public IObservable onErrorReturn(Object resumeFunction); - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the final result from the final call to your closure as its sole - * output. - *

          - * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," - * "compress," or "inject" in other programming contexts. Groovy, for instance, has an - * inject method that does a similar operation on lists. - *

          - * - * - * @param accumulator - * An accumulator closure to be invoked on each element from the sequence, whose result - * will be used in the next accumulator call (if applicable). - * - * @return An observable sequence with a single element from the result of accumulating the - * output from the list of Watchables. - * @see MSDN: - * Observable.Aggregate - * @see Wikipedia: Fold - * (higher-order function) - */ - public IObservable reduce(Func2 accumulator); - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the final result from the final call to your closure as its sole - * output. - *

          - * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," - * "compress," or "inject" in other programming contexts. Groovy, for instance, has an - * inject method that does a similar operation on lists. - *

          - * - * - * @param accumulator - * An accumulator closure to be invoked on each element from the sequence, whose result - * will be used in the next accumulator call (if applicable). - * - * @return A Watchable that emits a single element from the result of accumulating the output - * from the list of Watchables. - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public IObservable reduce(Object accumulator); - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the final result from the final call to your closure as its sole - * output. - *

          - * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," - * "compress," or "inject" in other programming contexts. Groovy, for instance, has an - * inject method that does a similar operation on lists. - *

          - * - * - * @param initialValue - * The initial (seed) accumulator value. - * @param accumulator - * An accumulator closure to be invoked on each element from the sequence, whose - * result will be used in the next accumulator call (if applicable). - * - * @return A Watchable that emits a single element from the result of accumulating the output - * from the list of Watchables. - * @see MSDN: - * Observable.Aggregate - * @see Wikipedia: Fold - * (higher-order function) - */ - public IObservable reduce(T initialValue, Func2 accumulator); - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the final result from the final call to your closure as its sole - * output. - *

          - * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," - * "compress," or "inject" in other programming contexts. Groovy, for instance, has an - * inject method that does a similar operation on lists. - *

          - * - * - * @param initialValue - * The initial (seed) accumulator value. - * @param accumulator - * An accumulator closure to be invoked on each element from the sequence, whose - * result will be used in the next accumulator call (if applicable). - * @return A Watchable that emits a single element from the result of accumulating the output - * from the list of Watchables. - * @see MSDN: - * Observable.Aggregate - * @see Wikipedia: Fold - * (higher-order function) - */ - public IObservable reduce(T initialValue, Object accumulator); - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the result of each of these iterations. It emits the result of - * each of these iterations as a sequence from the returned Watchable. This sort of closure is - * sometimes called an accumulator. - *

          - * - * - * @param accumulator - * An accumulator closure to be invoked on each element from the sequence whose - * result will be sent via onNext and used in the next accumulator call - * (if applicable). - * @return A Watchable sequence whose elements are the result of accumulating the output from - * the list of Watchables. - * @see MSDN: - * Observable.Scan - */ - public IObservable scan(Func2 accumulator); - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the result of each of these iterations. It emits the result of - * each of these iterations as a sequence from the returned Watchable. This sort of closure is - * sometimes called an accumulator. - *

          - * - * - * @param accumulator - * An accumulator closure to be invoked on each element from the sequence whose - * result will be sent via onNext and used in the next accumulator call - * (if applicable). - * - * @return A Watchable sequence whose elements are the result of accumulating the output from - * the list of Watchables. - * @see MSDN: - * Observable.Scan - */ - public IObservable scan(Object accumulator); - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, and so on until all items have been emitted by the - * source Watchable, emitting the result of each of these iterations. This sort of closure is - * sometimes called an accumulator. - *

          - * - * - * @param initialValue - * The initial (seed) accumulator value. - * @param accumulator - * An accumulator closure to be invoked on each element from the sequence whose - * result will be sent via onNext and used in the next accumulator call - * (if applicable). - * @return A Watchable sequence whose elements are the result of accumulating the output from - * the list of Watchables. - * @see MSDN: - * Observable.Scan - */ - public IObservable scan(T initialValue, Func2 accumulator); - - /** - * Returns a Watchable that applies a closure of your choosing to the first item emitted by a - * source Watchable, then feeds the result of that closure along with the second item emitted - * by a Watchable into the same closure, then feeds the result of that closure along with the - * third item into the same closure, and so on, emitting the result of each of these - * iterations. This sort of closure is sometimes called an accumulator. - *

          - * - * - * @param initialValue - * The initial (seed) accumulator value. - * @param accumulator - * An accumulator closure to be invoked on each element from the sequence whose result - * will be sent via onNext and used in the next accumulator call (if - * applicable). - * @return A Watchable sequence whose elements are the result of accumulating the output from - * the list of Watchables. - * @see MSDN: Observable.Scan - */ - public IObservable scan(T initialValue, Object accumulator); - - /** - * Returns a Watchable that skips the first num items emitted by the source - * Watchable. - * You can ignore the first num items emitted by a watchable and attend only to - * those items that come after, by modifying the watchable with the skip method. - *

          - * - * - * @param num - * The number of items to skip - * @return A Watchable sequence that is identical to the source Watchable except that it does - * not emit the first num items from that sequence. - */ - public IObservable skip(int num); - - /** - * Helper method that acts as a synonym to execute(APIServiceCallback callback) - * so that Groovy can pass in a closure without the - * as com.netflix.api.service.APIServiceCallback at the end of it. - * - * @param callbacks - */ - public IDisposable subscribe(Map callbacks); - - public IDisposable subscribe(Object onNext); - - public IDisposable subscribe(Object onNext, Object onError); - - public IDisposable subscribe(Object onNext, Object onError, Object onComplete); - - /** - * Returns a Watchable that emits the first num items emitted by the source - * Watchable. - * - * You can choose to pay attention only to the first num values emitted by a - * Watchable by calling its take method. This method returns a Watchable that will - * call a subscribing Watcher's onNext closure a maximum of num times - * before calling onCompleted. - *

          - * - * - * @param num - * @return a Watchable that emits only the first num items from the source - * watchable, or all of the items from the source Watchable if that Watchable emits - * fewer than num items. - */ - public IObservable take(int num); - - /** - * Returns a Watchable that emits a single item, a list composed of all the items emitted by - * the source Watchable. - * - * Normally, a Watchable that returns multiple items will do so by calling its Watcher's - * onNext closure for each such item. You can change this behavior, instructing - * the Watchable to compose a list of all of these multiple items and then to call the - * Watcher's onNext closure once, passing it the entire list, by calling the - * Watchable object's toList method prior to calling its subscribe - * method. - *

          - * - * - * @return a Watchable that emits a single item: a List containing all of the items emitted by - * the source Watchable. - */ - public IObservable> toList(); - - /** - * Sort T objects by their natural order (object must implement Comparable). - *

          - * - * - * @throws ClassCastException - * if T objects do not implement Comparable - * @return - */ - public IObservable> toSortedList(); - - /** - * Sort T objects using the defined sort function. - *

          - * - * - * @param sortFunction - * @return - */ - public IObservable> toSortedList(Func2 sortFunction); - - /** - * Sort T objects using the defined sort function. - *

          - * - * - * @param sortFunction - * @return - */ - public IObservable> toSortedList(final Object sortFunction); - -} diff --git a/rxjava-core/src/main/java/org/rx/reactive/IObserver.java b/rxjava-core/src/main/java/org/rx/reactive/IObserver.java deleted file mode 100644 index 5fb76baf13..0000000000 --- a/rxjava-core/src/main/java/org/rx/reactive/IObserver.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.rx.reactive; - -/** - * Provides a mechanism for receiving push-based notifications. - *

          - * After an Watcher calls a Watchable's IObservable.subscribe method, the Watchable calls the Watcher's onNext method to provide notifications. A well-behaved Watchable will call a Watcher's onCompleted closure exactly once or the Watcher's onError closure exactly once. - *

          - * For more informationon, see: API.Next Programmer's Guide: Watchers, Watchables, and the Reactive Pattern: Setting up Watchers in Groovy - * - * @param - */ -public interface IObserver { - - /** - * Notifies the Watcher that the Watchable has finished sending push-based notifications. - *

          - * The Watchable will not call this closure if it calls onError. - */ - public void onCompleted(); - - /** - * Notifies the Watcher that the Watchable has experienced an error condition. - *

          - * If the Watchable calls this closure, it will not thereafter call onNext or onCompleted. - * - * @param e - */ - public void onError(Exception e); - - /** - * Provides the Watcher with new data. - *

          - * The Watchable calls this closure 1 or more times, unless it calls onError in which case this closure may never be called. - *

          - * The Watchable will not call this closure again after it calls either onCompleted or onError, though this does not guarantee that chronologically-speaking, this closure will not be called after one of those closures is called (because the Watchable may assign the calling of these closures to chronologically-independent threads). See wx.synchronize() for information on how to enforce chronologically-ordered behavior. - * - * @param args - */ - public void onNext(T args); -} diff --git a/rxjava-core/src/main/java/org/rx/reactive/None.java b/rxjava-core/src/main/java/org/rx/reactive/None.java deleted file mode 100644 index 2be90043b6..0000000000 --- a/rxjava-core/src/main/java/org/rx/reactive/None.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.rx.reactive; - -/** - * A common sentinel object to represent NULL as an object. - * - * @ExcludeFromSDKJavadoc - */ -public class None { - - public static final None INSTANCE = new None(); - - private None() { - // prevent public instantiation - } -} diff --git a/rxjava-core/src/main/java/org/rx/reactive/Notification.java b/rxjava-core/src/main/java/org/rx/reactive/Notification.java index 2352a714d1..9840f230eb 100644 --- a/rxjava-core/src/main/java/org/rx/reactive/Notification.java +++ b/rxjava-core/src/main/java/org/rx/reactive/Notification.java @@ -1,7 +1,7 @@ package org.rx.reactive; /** - * An object representing a notification sent to a Watchable. + * An object representing a notification sent to a Observable. * * For the Microsoft Rx equivalent see: http://msdn.microsoft.com/en-us/library/hh229462(v=vs.103).aspx */ diff --git a/rxjava-core/src/main/java/org/rx/reactive/Observable.java b/rxjava-core/src/main/java/org/rx/reactive/Observable.java new file mode 100644 index 0000000000..dc36981e29 --- /dev/null +++ b/rxjava-core/src/main/java/org/rx/reactive/Observable.java @@ -0,0 +1,2331 @@ +package org.rx.reactive; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import groovy.lang.Binding; +import groovy.lang.GroovyClassLoader; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.codehaus.groovy.runtime.InvokerHelper; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.rx.functions.Func1; +import org.rx.functions.Func2; +import org.rx.functions.Func3; +import org.rx.functions.Func4; +import org.rx.functions.Functions; +import org.rx.operations.OperationFilter; +import org.rx.operations.OperationLast; +import org.rx.operations.OperationMap; +import org.rx.operations.OperationMaterialize; +import org.rx.operations.OperationMerge; +import org.rx.operations.OperationMergeDelayError; +import org.rx.operations.OperationOnErrorResumeNextViaFunction; +import org.rx.operations.OperationOnErrorResumeNextViaObservable; +import org.rx.operations.OperationOnErrorReturn; +import org.rx.operations.OperationScan; +import org.rx.operations.OperationSkip; +import org.rx.operations.OperationSynchronize; +import org.rx.operations.OperationTake; +import org.rx.operations.OperationToObservableFunction; +import org.rx.operations.OperationToObservableIterable; +import org.rx.operations.OperationToObservableList; +import org.rx.operations.OperationToObservableSortedList; +import org.rx.operations.OperationZip; + +/** + * The Observable interface that implements the Reactive Pattern. + *

          + * The documentation for this interface makes use of marble diagrams. The following legend explains + * these diagrams: + *

          + * + *

          + * It provides overloaded methods for subscribing as well as delegate methods to the + * + * @param + */ +public abstract class Observable { + + public Observable() { + } + + public static Observable create(final Func1> f, Observer observer) { + return new Observable() { + @Override + public Subscription subscribe(Observer observer) { + return f.call(observer); + } + }; + } + + /** + * A Observer must call a Observable's subscribe method in order to register itself + * to receive push-based notifications from the Observable. A typical implementation of the + * subscribe method does the following: + * + * It stores a reference to the Observer in a collection object, such as a List + * object. + * + * It returns a reference to an ObservableSubscription interface. This enables + * Observers to unsubscribe (that is, to stop receiving notifications) before the Observable has + * finished sending them and has called the Observer's OnCompleted method. + * + * At any given time, a particular instance of an Observable implementation is + * responsible for accepting all subscriptions and notifying all subscribers. Unless the + * documentation for a particular Observable implementation indicates otherwise, + * Observers should make no assumptions about the Observable implementation, such + * as the order of notifications that multiple Observers will receive. + *

          + * For more information see + * API.Next Programmer's Guide: Observers, Observables, and the Reactive Pattern + * + * @param Observer + * @return a ObservableSubscription reference to an interface that allows observers + * to stop receiving notifications before the provider has finished sending them + */ + public abstract Subscription subscribe(Observer observer); + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Subscription subscribe(final Map callbacks) { + return subscribe(new Observer() { + + public void onCompleted() { + Object completed = callbacks.get("onCompleted"); + if (completed != null) { + executeCallback(completed); + } + } + + public void onError(Exception e) { + handleError(e); + Object onError = callbacks.get("onError"); + if (onError != null) { + executeCallback(onError, e); + } + } + + public void onNext(Object args) { + Object onNext = callbacks.get("onNext"); + if (onNext == null) { + throw new RuntimeException("onNext must be implemented"); + } + executeCallback(onNext, args); + } + + }); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Subscription subscribe(final Object onNext) { + if (onNext instanceof Observer) { + throw new RuntimeException("Observers are not intended to be passed to this generic method. Your generic type is most likely wrong. This method is for dynamic code to send in closures."); + } + return subscribe(new Observer() { + + public void onCompleted() { + // do nothing + } + + public void onError(Exception e) { + handleError(e); + // no callback defined + } + + public void onNext(Object args) { + if (onNext == null) { + throw new RuntimeException("onNext must be implemented"); + } + executeCallback(onNext, args); + } + + }); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Subscription subscribe(final Object onNext, final Object onError) { + return subscribe(new Observer() { + + public void onCompleted() { + // do nothing + } + + public void onError(Exception e) { + handleError(e); + if (onError != null) { + executeCallback(onError, e); + } + } + + public void onNext(Object args) { + if (onNext == null) { + throw new RuntimeException("onNext must be implemented"); + } + executeCallback(onNext, args); + } + + }); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Subscription subscribe(final Object onNext, final Object onError, final Object onComplete) { + return subscribe(new Observer() { + + public void onCompleted() { + if (onComplete != null) { + executeCallback(onComplete); + } + } + + public void onError(Exception e) { + handleError(e); + if (onError != null) { + executeCallback(onError, e); + } + } + + public void onNext(Object args) { + if (onNext == null) { + throw new RuntimeException("onNext must be implemented"); + } + executeCallback(onNext, args); + } + + }); + } + + /** + * When an error occurs in any Observer we will invoke this to allow it to be handled by the global APIObservableErrorHandler + * + * @param e + */ + private static void handleError(Exception e) { + // plugins have been removed during opensourcing but the intention is to provide these hooks again + } + + /** + * Execute the callback with the given arguments. + *

          + * The callbacks align with the onCompleted, onError and onNext methods an an IObserver. + * + * @param callback + * Object to be invoked. It is left to the implementing class to determine the type, such as a Groovy Closure or JRuby closure conversion. + * @param args + */ + private void executeCallback(final Object callback, Object... args) { + Functions.execute(callback, args); + } + + /** + * A Observable that never sends any information to a Observer. + * + * This Observable is useful primarily for testing purposes. + * + * @param + * the type of item emitted by the Observable + */ + private static class NeverObservable extends Observable { + public Subscription subscribe(Observer observer) { + return new NullObservableSubscription(); + } + } + + /** + * A disposable object that does nothing when its unsubscribe method is called. + */ + private static class NullObservableSubscription implements Subscription { + public void unsubscribe() { + } + } + + /** + * A Observable that calls a Observer's onError closure when the Observer subscribes. + * + * @param + * the type of object returned by the Observable + */ + private static class ThrowObservable extends Observable { + private Exception exception; + + public ThrowObservable(Exception exception) { + this.exception = exception; + } + + /** + * Accepts a Observer and calls its onError method. + * + * @param observer + * a Observer of this Observable + * @return a reference to the subscription + */ + public Subscription subscribe(Observer observer) { + observer.onError(this.exception); + return new NullObservableSubscription(); + } + } + + /** + * Creates a Observable that will execute the given function when a Observer subscribes to it. + *

          + * You can create a simple Observable from scratch by using the create method. You pass this method a closure that accepts as a parameter the map of closures that a Observer passes to + * a + * Observable's subscribe method. Write + * the closure you pass to create so that it behaves as a Observable - calling the passed-in onNext, onError, and onCompleted methods + * appropriately. + *

          + * A well-formed Observable must call either the Observer's onCompleted method exactly once or its onError method exactly once. + * + * @param + * the type emitted by the Observable sequence + * @param func + * a closure that accepts a Observer and calls its onNext, onError, and onCompleted methods + * as appropriate, and returns a ObservableSubscription to allow + * cancelling the subscription (if applicable) + * @return a Observable that, when a Observer subscribes to it, will execute the given function + */ + public static Observable create(Func1> func) { + return wrap(OperationToObservableFunction.toObservableFunction(func)); + } + + /** + * Creates a Observable that will execute the given function when a Observer subscribes to it. + *

          + * You can create a simple Observable from scratch by using the create method. You pass this method a closure that accepts as a parameter the map of closures that a Observer passes to + * a + * Observable's subscribe method. Write + * the closure you pass to create so that it behaves as a Observable - calling the passed-in onNext, onError, and onCompleted methods + * appropriately. + *

          + * A well-formed Observable must call either the Observer's onCompleted method exactly once or its onError method exactly once. + * + * @param + * the type of the observable sequence + * @param func + * a closure that accepts a Observer and calls its onNext, onError, and onCompleted methods + * as appropriate, and returns a ObservableSubscription to allow + * cancelling the subscription (if applicable) + * @return a Observable that, when a Observer subscribes to it, will execute the given function + */ + public static Observable create(final Object callback) { + return create(new Func1>() { + + @Override + public Subscription call(Observer t1) { + return Functions.execute(callback, t1); + } + + }); + } + + /** + * Returns a Observable that returns no data to the Observer and immediately invokes its onCompleted method. + *

          + * + * + * @param + * the type of item emitted by the Observable + * @return a Observable that returns no data to the Observer and immediately invokes the + * Observer's onCompleted method + */ + public static Observable empty() { + return toObservable(new ArrayList()); + } + + /** + * Returns a Observable that calls onError when a Observer subscribes to it. + *

          + * Note: Maps to Observable.Throw in Rx - throw is a reserved word in Java. + *

          + * + * + * @param exception + * the error to throw + * @param + * the type of object returned by the Observable + * @return a Observable object that calls onError when a Observer subscribes + */ + public static Observable error(Exception exception) { + return wrap(new ThrowObservable(exception)); + } + + /** + * Filters a Observable by discarding any of its emissions that do not meet some test. + *

          + * + * + * @param that + * the Observable to filter + * @param predicate + * a closure that evaluates the items emitted by the source Observable, returning true if they pass the filter + * @return a Observable that emits only those items in the original Observable that the filter + * evaluates as true + */ + public static Observable filter(Observable that, Func1 predicate) { + return OperationFilter.filter(that, predicate); + } + + /** + * Filters a Observable by discarding any of its emissions that do not meet some test. + *

          + * + * + * @param that + * the Observable to filter + * @param predicate + * a closure that evaluates the items emitted by the source Observable, returning true if they pass the filter + * @return a Observable that emits only those items in the original Observable that the filter + * evaluates as true + */ + public static Observable filter(Observable that, final Object function) { + return filter(that, new Func1() { + + @Override + public Boolean call(T t1) { + return Functions.execute(function, t1); + + } + + }); + } + + /** + * Returns a Observable that notifies an observer of a single value and then completes. + *

          + * To convert any object into a Observable that emits that object, pass that object into the just method. + *

          + * This is similar to the {@link toObservable} method, except that toObservable will convert an iterable object into a Observable that emits each of the items in the iterable, one at + * a + * time, while the just method would + * convert the iterable into a Observable that emits the entire iterable as a single item. + *

          + * This value is the equivalent of Observable.Return in the Reactive Extensions library. + *

          + * + * + * @param value + * the value to pass to the Observer's onNext method + * @param + * the type of the value + * @return a Observable that notifies a Observer of a single value and then completes + */ + public static Observable just(T value) { + List list = new ArrayList(); + list.add(value); + + return toObservable(list); + } + + /** + * Takes the last item emitted by a source Observable and returns a Observable that emits only + * that item as its sole emission. + *

          + * To convert a Observable that emits a sequence of objects into one that only emits the last object in this sequence before completing, use the last method. + *

          + * + * + * @param that + * the source Observable + * @return a Observable that emits a single item, which is identical to the last item emitted + * by the source Observable + */ + public static Observable last(final Observable that) { + return wrap(OperationLast.last(that)); + } + + /** + * Applies a closure of your choosing to every notification emitted by a Observable, and returns + * this transformation as a new Observable sequence. + *

          + * + * + * @param sequence + * the source Observable + * @param func + * a closure to apply to each item in the sequence emitted by the source Observable + * @param + * the type of items emitted by the the source Observable + * @param + * the type of items returned by map closure func + * @return a Observable that is the result of applying the transformation function to each item + * in the sequence emitted by the source Observable + */ + public static Observable map(Observable sequence, Func1 func) { + return wrap(OperationMap.map(sequence, func)); + } + + /** + * Applies a closure of your choosing to every notification emitted by a Observable, and returns + * this transformation as a new Observable sequence. + *

          + * + * + * @param sequence + * the source Observable + * @param function + * a closure to apply to each item in the sequence emitted by the source Observable + * @param + * the type of items emitted by the the source Observable + * @param + * the type of items returned by map closure function + * @return a Observable that is the result of applying the transformation function to each item + * in the sequence emitted by the source Observable + */ + public static Observable map(Observable sequence, final Object function) { + return map(sequence, new Func1() { + + @Override + public R call(T t1) { + return Functions.execute(function, t1); + } + + }); + } + + /** + * Creates a new Observable sequence by applying a closure that you supply to each object in the + * original Observable sequence, where that closure is itself a Observable that emits objects, + * and then merges the results of that closure applied to every item emitted by the original + * Observable, emitting these merged results as its own sequence. + *

          + * + * + * @param sequence + * the source Observable + * @param func + * a closure to apply to each item emitted by the source Observable, generating a + * Observable + * @param + * the type emitted by the source Observable + * @param + * the type emitted by the Observables emitted by func + * @return a Observable that emits a sequence that is the result of applying the transformation + * function to each item emitted by the source Observable and merging the results of + * the Observables obtained from this transformation + */ + public static Observable mapMany(Observable sequence, Func1, T> func) { + return wrap(OperationMap.mapMany(sequence, func)); + } + + /** + * Creates a new Observable sequence by applying a closure that you supply to each object in the + * original Observable sequence, where that closure is itself a Observable that emits objects, + * and then merges the results of that closure applied to every item emitted by the original + * Observable, emitting these merged results as its own sequence. + *

          + * + * + * @param sequence + * the source Observable + * @param function + * a closure to apply to each item emitted by the source Observable, generating a + * Observable + * @param + * the type emitted by the source Observable + * @param + * the type emitted by the Observables emitted by function + * @return a Observable that emits a sequence that is the result of applying the transformation + * function to each item emitted by the source Observable and merging the results of the + * Observables obtained from this transformation + */ + public static Observable mapMany(Observable sequence, final Object function) { + return mapMany(sequence, new Func1() { + + @Override + public R call(T t1) { + return Functions.execute(function, t1); + } + + }); + } + + /** + * Materializes the implicit notifications of an observable sequence as explicit notification values. + *

          + * + * + * @param source + * An observable sequence of elements to project. + * @return An observable sequence whose elements are the result of materializing the notifications of the given sequence. + * @see http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx + */ + public static Observable> materialize(final Observable sequence) { + return OperationMaterialize.materialize(sequence); + } + + /** + * Flattens the Observable sequences from a list of Observables into one Observable sequence + * without any transformation. You can combine the output of multiple Observables so that they + * act like a single Observable, by using the merge method. + *

          + * + * + * @param source + * a list of Observables that emit sequences of items + * @return a Observable that emits a sequence of elements that are the result of flattening the + * output from the source list of Observables + * @see MSDN: Observable.Merge Method + */ + public static Observable merge(List> source) { + return wrap(OperationMerge.merge(source)); + } + + /** + * Flattens the Observable sequences emitted by a sequence of Observables that are emitted by a + * Observable into one Observable sequence without any transformation. You can combine the output + * of multiple Observables so that they act like a single Observable, by using the merge method. + *

          + * + * + * @param source + * a Observable that emits Observables + * @return a Observable that emits a sequence of elements that are the result of flattening the + * output from the Observables emitted by the source Observable + * @see MSDN: Observable.Merge Method + */ + public static Observable merge(Observable> source) { + return wrap(OperationMerge.merge(source)); + } + + /** + * Flattens the Observable sequences from a series of Observables into one Observable sequence + * without any transformation. You can combine the output of multiple Observables so that they + * act like a single Observable, by using the merge method. + *

          + * + * + * @param source + * a series of Observables that emit sequences of items + * @return a Observable that emits a sequence of elements that are the result of flattening the + * output from the source Observables + * @see MSDN: Observable.Merge Method + */ + public static Observable merge(Observable... source) { + return wrap(OperationMerge.merge(source)); + } + + /** + * Same functionality as merge except that errors received to onError will be held until all sequences have finished (onComplete/onError) before sending the error. + *

          + * Only the first onError received will be sent. + *

          + * This enables receiving all successes from merged sequences without one onError from one sequence causing all onNext calls to be prevented. + *

          + * + * + * @param source + * a list of Observables that emit sequences of items + * @return a Observable that emits a sequence of elements that are the result of flattening the + * output from the source list of Observables + * @see MSDN: Observable.Merge Method + */ + public static Observable mergeDelayError(List> source) { + return wrap(OperationMergeDelayError.mergeDelayError(source)); + } + + /** + * Same functionality as merge except that errors received to onError will be held until all sequences have finished (onComplete/onError) before sending the error. + *

          + * Only the first onError received will be sent. + *

          + * This enables receiving all successes from merged sequences without one onError from one sequence causing all onNext calls to be prevented. + *

          + * + * + * @param source + * a Observable that emits Observables + * @return a Observable that emits a sequence of elements that are the result of flattening the + * output from the Observables emitted by the source Observable + * @see MSDN: Observable.Merge Method + */ + public static Observable mergeDelayError(Observable> source) { + return wrap(OperationMergeDelayError.mergeDelayError(source)); + } + + /** + * Same functionality as merge except that errors received to onError will be held until all sequences have finished (onComplete/onError) before sending the error. + *

          + * Only the first onError received will be sent. + *

          + * This enables receiving all successes from merged sequences without one onError from one sequence causing all onNext calls to be prevented. + *

          + * + * + * @param source + * a series of Observables that emit sequences of items + * @return a Observable that emits a sequence of elements that are the result of flattening the + * output from the source Observables + * @see MSDN: Observable.Merge Method + */ + public static Observable mergeDelayError(Observable... source) { + return wrap(OperationMergeDelayError.mergeDelayError(source)); + } + + /** + * Returns a Observable that never sends any information to a Observer. + * + * This observable is useful primarily for testing purposes. + *

          + * + * + * @param + * the type of item (not) emitted by the Observable + * @return a Observable that never sends any information to a Observer + */ + public static Observable never() { + return wrap(new NeverObservable()); + } + + /** + * A ObservableSubscription that does nothing. + * + * @return + */ + public static Subscription noOpSubscription() { + return new NullObservableSubscription(); + } + + /** + * Instruct a Observable to pass control to another Observable (the return value of a function) + * rather than calling onError if it encounters an error. + *

          + * By default, when a Observable encounters an error that prevents it from emitting the expected item to its Observer, the Observable calls its Observer's onError closure, and then + * quits + * without calling any more of its Observer's + * closures. The onErrorResumeNext method changes this behavior. If you pass a closure that emits a Observable (resumeFunction) to a Observable's + * onErrorResumeNext method, if the original Observable encounters + * an error, instead of calling its Observer's onError closure, it will instead relinquish control to this new Observable, which will call the Observer's onNext method if + * it + * is able to do so. In such a case, because no + * Observable necessarily invokes onError, the Observer may never know that an error happened. + *

          + * You can use this to prevent errors from propagating or to supply fallback data should errors be encountered. + *

          + * + * + * @param that + * the source Observable + * @param resumeFunction + * a closure that returns a Observable that will take over if the source Observable + * encounters an error + * @return the source Observable, with its behavior modified as described + */ + public static Observable onErrorResumeNext(final Observable that, final Func1, Exception> resumeFunction) { + return wrap(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(that, resumeFunction)); + } + + /** + * Instruct a Observable to emit a particular object (as returned by a closure) to its Observer + * rather than calling onError if it encounters an error. + *

          + * By default, when a Observable encounters an error that prevents it from emitting the expected item to its Observer, the Observable calls its Observer's onError closure, and then + * quits + * without calling any more of its Observer's + * closures. The onErrorResumeNext method changes this behavior. If you pass a function that returns another Observable (resumeFunction) to a Observable's + * onErrorResumeNext method, if the original Observable + * encounters an error, instead of calling its Observer's onError closure, it will instead relinquish control to this new Observable which will call the Observer's onNext + * method if it is able to do so. In such a case, + * because no Observable necessarily invokes onError, the Observer may never know that an error happened. + *

          + * You can use this to prevent errors from propagating or to supply fallback data should errors be encountered. + *

          + * + * + * @param that + * the source Observable + * @param resumeFunction + * a closure that returns a Observable that will take over if the source Observable + * encounters an error + * @return the source Observable, with its behavior modified as described + */ + public static Observable onErrorResumeNext(final Observable that, final Object resumeFunction) { + return onErrorResumeNext(that, new Func1, Exception>() { + + @Override + public Observable call(Exception e) { + return Functions.execute(resumeFunction, e); + } + }); + } + + /** + * Instruct a Observable to pass control to another Observable rather than calling onError if it encounters an error. + *

          + * By default, when a Observable encounters an error that prevents it from emitting the expected item to its Observer, the Observable calls its Observer's onError closure, and then + * quits + * without calling any more of its Observer's + * closures. The onErrorResumeNext method changes this behavior. If you pass another Observable (resumeSequence) to a Observable's onErrorResumeNext method, + * if + * the original Observable encounters an error, + * instead of calling its Observer's onError closure, it will instead relinquish control to resumeSequence which will call the Observer's onNext method if it + * is able to do so. In such a case, because no + * Observable necessarily invokes onError, the Observer may never know that an error happened. + *

          + * You can use this to prevent errors from propagating or to supply fallback data should errors be encountered. + *

          + * + * + * @param that + * the source Observable + * @param resumeSequence + * the Observable that will take over if the source Observable encounters an error + * @return the source Observable, with its behavior modified as described + */ + public static Observable onErrorResumeNext(final Observable that, final Observable resumeSequence) { + return wrap(OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable(that, resumeSequence)); + } + + /** + * Instruct a Observable to emit a particular item to its Observer's onNext closure + * rather than calling onError if it encounters an error. + *

          + * By default, when a Observable encounters an error that prevents it from emitting the expected item to its Observer, the Observable calls its Observer's onError closure, and then + * quits + * without calling any more of its Observer's + * closures. The onErrorReturn method changes this behavior. If you pass a closure (resumeFunction) to a Observable's onErrorReturn method, if the original + * Observable encounters an error, instead of calling + * its Observer's onError closure, it will instead pass the return value of resumeFunction to the Observer's onNext method. + *

          + * You can use this to prevent errors from propagating or to supply fallback data should errors be encountered. + *

          + * + * + * @param that + * the source Observable + * @param resumeFunction + * a closure that returns a value that will be passed into a Observer's onNext closure if the Observable encounters an error that would + * otherwise cause it to call onError + * @return the source Observable, with its behavior modified as described + */ + public static Observable onErrorReturn(final Observable that, Func1 resumeFunction) { + return wrap(OperationOnErrorReturn.onErrorReturn(that, resumeFunction)); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the final result from the final call to your closure as its sole + * output. + *

          + * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, has an inject + * method that does a similar operation on lists. + *

          + * + * + * @param + * the type item emitted by the source Observable + * @param sequence + * the source Observable + * @param accumulator + * an accumulator closure to be invoked on each element from the sequence, whose + * result will be used in the next accumulator call (if applicable) + * + * @return a Observable that emits a single element that is the result of accumulating the + * output from applying the accumulator to the sequence of items emitted by the source + * Observable + * @see MSDN: Observable.Aggregate + * @see Wikipedia: Fold (higher-order function) + */ + public static Observable reduce(Observable sequence, Func2 accumulator) { + return wrap(OperationScan.scan(sequence, accumulator).last()); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the final result from the final call to your closure as its sole + * output. + *

          + * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, has an inject + * method that does a similar operation on lists. + *

          + * + * + * @param + * the type item emitted by the source Observable + * @param sequence + * the source Observable + * @param accumulator + * an accumulator closure to be invoked on each element from the sequence, whose + * result will be used in the next accumulator call (if applicable) + * + * @return a Observable that emits a single element that is the result of accumulating the + * output from applying the accumulator to the sequence of items emitted by the source + * Observable + * @see MSDN: Observable.Aggregate + * @see Wikipedia: Fold (higher-order function) + */ + public static Observable reduce(final Observable sequence, final Object accumulator) { + return reduce(sequence, new Func2() { + + @Override + public T call(T t1, T t2) { + return Functions.execute(accumulator, t1, t2); + } + + }); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the final result from the final call to your closure as its sole + * output. + *

          + * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, has an inject + * method that does a similar operation on lists. + *

          + * + * + * @param + * the type item emitted by the source Observable + * @param sequence + * the source Observable + * @param initialValue + * a seed passed into the first execution of the accumulator closure + * @param accumulator + * an accumulator closure to be invoked on each element from the sequence, whose + * result will be used in the next accumulator call (if applicable) + * + * @return a Observable that emits a single element that is the result of accumulating the + * output from applying the accumulator to the sequence of items emitted by the source + * Observable + * @see MSDN: Observable.Aggregate + * @see Wikipedia: Fold (higher-order function) + */ + public static Observable reduce(Observable sequence, T initialValue, Func2 accumulator) { + return wrap(OperationScan.scan(sequence, initialValue, accumulator).last()); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the final result from the final call to your closure as its sole + * output. + *

          + * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, has an inject + * method that does a similar operation on lists. + *

          + * + * + * @param + * the type item emitted by the source Observable + * @param sequence + * the source Observable + * @param initialValue + * a seed passed into the first execution of the accumulator closure + * @param accumulator + * an accumulator closure to be invoked on each element from the sequence, whose + * result will be used in the next accumulator call (if applicable) + * @return a Observable that emits a single element that is the result of accumulating the + * output from applying the accumulator to the sequence of items emitted by the source + * Observable + * @see MSDN: Observable.Aggregate + * @see Wikipedia: Fold (higher-order function) + */ + public static Observable reduce(final Observable sequence, final T initialValue, final Object accumulator) { + return reduce(sequence, initialValue, new Func2() { + + @Override + public T call(T t1, T t2) { + return Functions.execute(accumulator, t1, t2); + } + + }); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the result of each of these iterations as its own sequence. + *

          + * + * + * @param + * the type item emitted by the source Observable + * @param sequence + * the source Observable + * @param accumulator + * an accumulator closure to be invoked on each element from the sequence, whose + * result will be emitted and used in the next accumulator call (if applicable) + * @return a Observable that emits a sequence of items that are the result of accumulating the + * output from the sequence emitted by the source Observable + * @see MSDN: Observable.Scan + */ + public static Observable scan(Observable sequence, Func2 accumulator) { + return wrap(OperationScan.scan(sequence, accumulator)); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the result of each of these iterations as its own sequence. + *

          + * + * + * @param + * the type item emitted by the source Observable + * @param sequence + * the source Observable + * @param accumulator + * an accumulator closure to be invoked on each element from the sequence, whose + * result will be emitted and used in the next accumulator call (if applicable) + * @return a Observable that emits a sequence of items that are the result of accumulating the + * output from the sequence emitted by the source Observable + * @see MSDN: Observable.Scan + */ + public static Observable scan(final Observable sequence, final Object accumulator) { + return scan(sequence, new Func2() { + + @Override + public T call(T t1, T t2) { + return Functions.execute(accumulator, t1, t2); + } + + }); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the result of each of these iterations as its own sequence. + *

          + * + * + * @param + * the type item emitted by the source Observable + * @param sequence + * the source Observable + * @param initialValue + * the initial (seed) accumulator value + * @param accumulator + * an accumulator closure to be invoked on each element from the sequence, whose + * result will be emitted and used in the next accumulator call (if applicable) + * @return a Observable that emits a sequence of items that are the result of accumulating the + * output from the sequence emitted by the source Observable + * @see MSDN: Observable.Scan + */ + public static Observable scan(Observable sequence, T initialValue, Func2 accumulator) { + return wrap(OperationScan.scan(sequence, initialValue, accumulator)); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the result of each of these iterations as its own sequence. + *

          + * + * + * @param + * the type item emitted by the source Observable + * @param sequence + * the source Observable + * @param initialValue + * the initial (seed) accumulator value + * @param accumulator + * an accumulator closure to be invoked on each element from the sequence, whose + * result will be emitted and used in the next accumulator call (if applicable) + * @return a Observable that emits a sequence of items that are the result of accumulating the + * output from the sequence emitted by the source Observable + * @see MSDN: Observable.Scan + */ + public static Observable scan(final Observable sequence, final T initialValue, final Object accumulator) { + return scan(sequence, initialValue, new Func2() { + + @Override + public T call(T t1, T t2) { + return Functions.execute(accumulator, t1, t2); + } + + }); + } + + /** + * Returns a Observable that skips the first num items emitted by the source + * Observable. You can ignore the first num items emitted by a Observable and attend + * only to those items that come after, by modifying the Observable with the skip method. + *

          + * + * + * @param items + * the source Observable + * @param num + * the number of items to skip + * @return a Observable that emits the same sequence of items emitted by the source Observable, + * except for the first num items + * @see MSDN: Observable.Skip Method + */ + public static Observable skip(final Observable items, int num) { + return wrap(OperationSkip.skip(items, num)); + } + + /** + * Accepts a Observable and wraps it in another Observable that ensures that the resulting + * Observable is chronologically well-behaved. + *

          + * A well-behaved observable ensures onNext, onCompleted, or onError calls to its subscribers are not interleaved, onCompleted and + * onError are only called once respectively, and no + * onNext calls follow onCompleted and onError calls. + * + * @param observable + * the source Observable + * @param + * the type of item emitted by the source Observable + * @return a Observable that is a chronologically well-behaved version of the source Observable + */ + public static Observable synchronize(Observable observable) { + return wrap(OperationSynchronize.synchronize(observable)); + } + + /** + * Returns a Observable that emits the first num items emitted by the source + * Observable. + *

          + * You can choose to pay attention only to the first num values emitted by a Observable by calling its take method. This method returns a Observable that will call a + * subscribing Observer's onNext closure a + * maximum of num times before calling onCompleted. + *

          + * + * + * @param items + * the source Observable + * @param num + * the number of items from the start of the sequence emitted by the source + * Observable to emit + * @return a Observable that only emits the first num items emitted by the source + * Observable + */ + public static Observable take(final Observable items, final int num) { + return wrap(OperationTake.take(items, num)); + } + + /** + * Returns a Observable that emits a single item, a list composed of all the items emitted by + * the source Observable. + *

          + * Normally, a Observable that returns multiple items will do so by calling its Observer's onNext closure for each such item. You can change this behavior, instructing the Observable + * to + * compose a list of all of these multiple items and + * then to call the Observer's onNext closure once, passing it the entire list, by calling the Observable object's toList method prior to calling its + * subscribe + * method. + *

          + * + * + * @param that + * the source Observable + * @return a Observable that emits a single item: a List containing all of the + * items emitted by the source Observable + */ + public static Observable> toList(final Observable that) { + return wrap(OperationToObservableList.toObservableList(that)); + } + + /** + * Converts an Iterable sequence to a Observable sequence. + * + * Any object that supports the Iterable interface can be converted into a Observable that emits + * each iterable item in the object, by passing the object into the toObservable method. + *

          + * + * + * @param iterable + * the source Iterable sequence + * @param + * the type of items in the iterable sequence and the type emitted by the resulting + * Observable + * @return a Observable that emits each item in the source Iterable sequence + */ + public static Observable toObservable(Iterable iterable) { + return wrap(OperationToObservableIterable.toObservableIterable(iterable)); + } + + /** + * Converts an Array sequence to a Observable sequence. + * + * An Array can be converted into a Observable that emits each item in the Array, by passing the + * Array into the toObservable method. + *

          + * + * + * @param iterable + * the source Array + * @param + * the type of items in the Array, and the type of items emitted by the resulting + * Observable + * @return a Observable that emits each item in the source Array + */ + public static Observable toObservable(T... items) { + return toObservable(Arrays.asList(items)); + } + + /** + * Sort T objects by their natural order (object must implement Comparable). + *

          + * + * + * @param sequence + * @throws ClassCastException + * if T objects do not implement Comparable + * @return + */ + public static Observable> toSortedList(Observable sequence) { + return wrap(OperationToObservableSortedList.toSortedList(sequence)); + } + + /** + * Sort T objects using the defined sort function. + *

          + * + * + * @param sequence + * @param sortFunction + * @return + */ + public static Observable> toSortedList(Observable sequence, Func2 sortFunction) { + return wrap(OperationToObservableSortedList.toSortedList(sequence, sortFunction)); + } + + /** + * Sort T objects using the defined sort function. + *

          + * + * + * @param sequence + * @param sortFunction + * @return + */ + public static Observable> toSortedList(Observable sequence, final Object sortFunction) { + return wrap(OperationToObservableSortedList.toSortedList(sequence, new Func2() { + + @Override + public Integer call(T t1, T t2) { + return Functions.execute(sortFunction, t1, t2); + } + + })); + } + + /** + * Allow wrapping responses with the AbstractObservable so that we have all of + * the utility methods available for subscribing. + *

          + * This is not expected to benefit Java usage, but is intended for dynamic script which are a primary target of the Observable operations. + *

          + * Since they are dynamic they can execute the "hidden" methods on AbstractObservable while appearing to only receive an Observable without first casting. + * + * @param o + * @return + */ + private static Observable wrap(final Observable o) { + if (o instanceof Observable) { + // if the Observable is already an AbstractObservable, don't wrap it again. + return (Observable) o; + } + return new Observable() { + + @Override + public Subscription subscribe(Observer observer) { + return o.subscribe(observer); + } + + }; + } + + /** + * Returns a Observable that applies a closure of your choosing to the combination of items + * emitted, in sequence, by two other Observables, with the results of this closure becoming the + * sequence emitted by the returned Observable. + *

          + * zip applies this closure in strict sequence, so the first item emitted by the new Observable will be the result of the closure applied to the first item emitted by w0 + * and the first item emitted by w1; the + * second item emitted by the new Observable will be the result of the closure applied to the second item emitted by w0 and the second item emitted by w1; and so forth. + *

          + * The resulting Observable returned from zip will call onNext as many times as the number onNext calls of the source Observable with the + * shortest sequence. + *

          + * + * + * @param w0 + * one source Observable + * @param w1 + * another source Observable + * @param reduceFunction + * a closure that, when applied to an item emitted by each of the source Observables, + * results in a value that will be emitted by the resulting Observable + * @return a Observable that emits the zipped results + */ + public static Observable zip(Observable w0, Observable w1, Func2 reduceFunction) { + return wrap(OperationZip.zip(w0, w1, reduceFunction)); + } + + /** + * Returns a Observable that applies a closure of your choosing to the combination of items + * emitted, in sequence, by two other Observables, with the results of this closure becoming the + * sequence emitted by the returned Observable. + *

          + * zip applies this closure in strict sequence, so the first item emitted by the new Observable will be the result of the closure applied to the first item emitted by w0 + * and the first item emitted by w1; the + * second item emitted by the new Observable will be the result of the closure applied to the second item emitted by w0 and the second item emitted by w1; and so forth. + *

          + * The resulting Observable returned from zip will call onNext as many times as the number onNext calls of the source Observable with the + * shortest sequence. + *

          + * + * + * @param w0 + * one source Observable + * @param w1 + * another source Observable + * @param reduceFunction + * a closure that, when applied to an item emitted by each of the source Observables, + * results in a value that will be emitted by the resulting Observable + * @return a Observable that emits the zipped results + */ + public static Observable zip(Observable w0, Observable w1, final Object function) { + return zip(w0, w1, new Func2() { + + @Override + public R call(T0 t0, T1 t1) { + return Functions.execute(function, t0, t1); + } + + }); + } + + /** + * Returns a Observable that applies a closure of your choosing to the combination of items + * emitted, in sequence, by three other Observables, with the results of this closure becoming + * the sequence emitted by the returned Observable. + *

          + * zip applies this closure in strict sequence, so the first item emitted by the new Observable will be the result of the closure applied to the first item emitted by w0, + * the first item emitted by w1, and the + * first item emitted by w2; the second item emitted by the new Observable will be the result of the closure applied to the second item emitted by w0, the second item + * emitted by w1, and the second item + * emitted by w2; and so forth. + *

          + * The resulting Observable returned from zip will call onNext as many times as the number onNext calls of the source Observable with the + * shortest sequence. + *

          + * + * + * @param w0 + * one source Observable + * @param w1 + * another source Observable + * @param w2 + * a third source Observable + * @param function + * a closure that, when applied to an item emitted by each of the source Observables, + * results in a value that will be emitted by the resulting Observable + * @return a Observable that emits the zipped results + */ + public static Observable zip(Observable w0, Observable w1, Observable w2, Func3 function) { + return wrap(OperationZip.zip(w0, w1, w2, function)); + } + + /** + * Returns a Observable that applies a closure of your choosing to the combination of items + * emitted, in sequence, by three other Observables, with the results of this closure becoming + * the sequence emitted by the returned Observable. + *

          + * zip applies this closure in strict sequence, so the first item emitted by the new Observable will be the result of the closure applied to the first item emitted by w0, + * the first item emitted by w1, and the + * first item emitted by w2; the second item emitted by the new Observable will be the result of the closure applied to the second item emitted by w0, the second item + * emitted by w1, and the second item + * emitted by w2; and so forth. + *

          + * The resulting Observable returned from zip will call onNext as many times as the number onNext calls of the source Observable with the + * shortest sequence. + *

          + * + * + * @param w0 + * one source Observable + * @param w1 + * another source Observable + * @param w2 + * a third source Observable + * @param function + * a closure that, when applied to an item emitted by each of the source Observables, + * results in a value that will be emitted by the resulting Observable + * @return a Observable that emits the zipped results + */ + public static Observable zip(Observable w0, Observable w1, Observable w2, final Object function) { + return zip(w0, w1, w2, new Func3() { + + @Override + public R call(T0 t0, T1 t1, T2 t2) { + return Functions.execute(function, t0, t1, t2); + } + + }); + } + + /** + * Returns a Observable that applies a closure of your choosing to the combination of items + * emitted, in sequence, by four other Observables, with the results of this closure becoming + * the sequence emitted by the returned Observable. + *

          + * zip applies this closure in strict sequence, so the first item emitted by the new Observable will be the result of the closure applied to the first item emitted by w0, + * the first item emitted by w1, the + * first item emitted by w2, and the first item emitted by w3; the second item emitted by the new Observable will be the result of the closure applied to the second item + * emitted by each of those Observables; and so forth. + *

          + * The resulting Observable returned from zip will call onNext as many times as the number onNext calls of the source Observable with the + * shortest sequence. + *

          + * + * + * @param w0 + * one source Observable + * @param w1 + * another source Observable + * @param w2 + * a third source Observable + * @param w3 + * a fourth source Observable + * @param reduceFunction + * a closure that, when applied to an item emitted by each of the source Observables, + * results in a value that will be emitted by the resulting Observable + * @return a Observable that emits the zipped results + */ + public static Observable zip(Observable w0, Observable w1, Observable w2, Observable w3, Func4 reduceFunction) { + return wrap(OperationZip.zip(w0, w1, w2, w3, reduceFunction)); + } + + /** + * Returns a Observable that applies a closure of your choosing to the combination of items + * emitted, in sequence, by four other Observables, with the results of this closure becoming + * the sequence emitted by the returned Observable. + *

          + * zip applies this closure in strict sequence, so the first item emitted by the new Observable will be the result of the closure applied to the first item emitted by w0, + * the first item emitted by w1, the + * first item emitted by w2, and the first item emitted by w3; the second item emitted by the new Observable will be the result of the closure applied to the second item + * emitted by each of those Observables; and so forth. + *

          + * The resulting Observable returned from zip will call onNext as many times as the number onNext calls of the source Observable with the + * shortest sequence. + *

          + * + * + * @param w0 + * one source Observable + * @param w1 + * another source Observable + * @param w2 + * a third source Observable + * @param w3 + * a fourth source Observable + * @param function + * a closure that, when applied to an item emitted by each of the source Observables, + * results in a value that will be emitted by the resulting Observable + * @return a Observable that emits the zipped results + */ + public static Observable zip(Observable w0, Observable w1, Observable w2, Observable w3, final Object function) { + return zip(w0, w1, w2, w3, new Func4() { + + @Override + public R call(T0 t0, T1 t1, T2 t2, T3 t3) { + return Functions.execute(function, t0, t1, t2, t3); + } + + }); + } + + /** + * Filters a Observable by discarding any of its emissions that do not meet some test. + *

          + * + * + * @param predicate + * a closure that evaluates the items emitted by the source Observable, returning + * true if they pass the filter + * @return a Observable that emits only those items in the original Observable that the filter + * evaluates as true + */ + public Observable filter(Func1 predicate) { + return filter(this, predicate); + } + + /** + * Filters a Observable by discarding any of its emissions that do not meet some test. + *

          + * + * + * @param callback + * a closure that evaluates the items emitted by the source Observable, returning + * true if they pass the filter + * @return a Observable that emits only those items in the original Observable that the filter + * evaluates as "true" + */ + public Observable filter(final Object callback) { + return filter(this, new Func1() { + + public Boolean call(T t1) { + return Functions.execute(callback, t1); + } + }); + } + + /** + * Converts a Observable that emits a sequence of objects into one that only emits the last + * object in this sequence before completing. + *

          + * + * + * @return a Observable that emits only the last item emitted by the original Observable + */ + public Observable last() { + return last(this); + } + + /** + * Applies a closure of your choosing to every item emitted by a Observable, and returns this + * transformation as a new Observable sequence. + *

          + * + * + * @param func + * a closure to apply to each item in the sequence. + * @return a Observable that emits a sequence that is the result of applying the transformation + * closure to each item in the sequence emitted by the input Observable. + */ + public Observable map(Func1 func) { + return map(this, func); + } + + /** + * Applies a closure of your choosing to every item emitted by a Observable, and returns this + * transformation as a new Observable sequence. + *

          + * + * + * @param callback + * a closure to apply to each item in the sequence. + * @return a Observable that emits a sequence that is the result of applying the transformation + * closure to each item in the sequence emitted by the input Observable. + */ + public Observable map(final Object callback) { + return map(this, new Func1() { + + public R call(T t1) { + return Functions.execute(callback, t1); + } + }); + } + + /** + * Creates a new Observable sequence by applying a closure that you supply to each item in the + * original Observable sequence, where that closure is itself a Observable that emits items, and + * then merges the results of that closure applied to every item emitted by the original + * Observable, emitting these merged results as its own sequence. + *

          + * + * + * @param func + * a closure to apply to each item in the sequence, that returns a Observable. + * @return a Observable that emits a sequence that is the result of applying the transformation + * function to each item in the input sequence and merging the results of the + * Observables obtained from this transformation. + */ + public Observable mapMany(Func1, T> func) { + return mapMany(this, func); + } + + /** + * Creates a new Observable sequence by applying a closure that you supply to each item in the + * original Observable sequence, where that closure is itself a Observable that emits items, and + * then merges the results of that closure applied to every item emitted by the original + * Observable, emitting these merged results as its own sequence. + *

          + * + * + * @param callback + * a closure to apply to each item in the sequence that returns a Observable. + * @return a Observable that emits a sequence that is the result of applying the transformation' + * function to each item in the input sequence and merging the results of the + * Observables obtained from this transformation. + */ + public Observable mapMany(final Object callback) { + return mapMany(this, new Func1, T>() { + + public Observable call(T t1) { + return Functions.execute(callback, t1); + } + }); + } + + /** + * Materializes the implicit notifications of this observable sequence as explicit notification values. + *

          + * + * + * @return An observable sequence whose elements are the result of materializing the notifications of the given sequence. + * @see http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx + */ + public Observable> materialize() { + return OperationMaterialize.materialize(this); + } + + /** + * Instruct a Observable to pass control to another Observable rather than calling onError if it encounters an error. + * + * By default, when a Observable encounters an error that prevents it from emitting the expected + * item to its Observer, the Observable calls its Observer's onError closure, and + * then quits without calling any more of its Observer's closures. The + * onErrorResumeNext method changes this behavior. If you pass another Observable + * (resumeFunction) to a Observable's onErrorResumeNext method, if the + * original Observable encounters an error, instead of calling its Observer's + * onErrort closure, it will instead relinquish control to + * resumeFunction which will call the Observer's onNext method if it + * is able to do so. In such a case, because no Observable necessarily invokes + * onError, the Observer may never know that an error happened. + * + * You can use this to prevent errors from propagating or to supply fallback data should errors + * be encountered. + *

          + * + * + * @param resumeFunction + * @return the original Observable, with appropriately modified behavior + */ + public Observable onErrorResumeNext(final Func1, Exception> resumeFunction) { + return onErrorResumeNext(this, resumeFunction); + } + + /** + * Instruct a Observable to emit a particular item rather than calling onError if + * it encounters an error. + * + * By default, when a Observable encounters an error that prevents it from emitting the expected + * item to its Observer, the Observable calls its Observer's onError closure, and + * then quits without calling any more of its Observer's closures. The + * onErrorResumeNext method changes this behavior. If you pass another Observable + * (resumeFunction) to a Observable's onErrorResumeNext method, if the + * original Observable encounters an error, instead of calling its Observer's + * onError closure, it will instead relinquish control to + * resumeFunction which will call the Observer's onNext method if it + * is able to do so. In such a case, because no Observable necessarily invokes + * onError, the Observer may never know that an error happened. + * + * You can use this to prevent errors from propagating or to supply fallback data should errors + * be encountered. + *

          + * + * + * @param resumeFunction + * @return the original Observable with appropriately modified behavior + */ + public Observable onErrorResumeNext(final Object resumeFunction) { + return onErrorResumeNext(this, new Func1, Exception>() { + + public Observable call(Exception e) { + return Functions.execute(resumeFunction, e); + } + }); + } + + /** + * Instruct a Observable to pass control to another Observable rather than calling + * onError if it encounters an error. + * + * By default, when a Observable encounters an error that prevents it from emitting the expected + * item to its Observer, the Observable calls its Observer's onError closure, and + * then quits without calling any more of its Observer's closures. The + * onErrorResumeNext method changes this behavior. If you pass another Observable + * (resumeSequence) to a Observable's onErrorResumeNext method, if the + * original Observable encounters an error, instead of calling its Observer's + * onError closure, it will instead relinquish control to + * resumeSequence which will call the Observer's onNext method if it + * is able to do so. In such a case, because no Observable necessarily invokes + * onError, the Observer may never know that an error happened. + * + * You can use this to prevent errors from propagating or to supply fallback data should errors + * be encountered. + *

          + * + * + * @param resumeSequence + * @return the original Observable, with appropriately modified behavior + */ + public Observable onErrorResumeNext(final Observable resumeSequence) { + return onErrorResumeNext(this, resumeSequence); + } + + /** + * Instruct a Observable to emit a particular item rather than calling onError if + * it encounters an error. + * + * By default, when a Observable encounters an error that prevents it from emitting the expected + * object to its Observer, the Observable calls its Observer's onError closure, and + * then quits without calling any more of its Observer's closures. The + * onErrorReturn method changes this behavior. If you pass a closure + * (resumeFunction) to a Observable's onErrorReturn method, if the + * original Observable encounters an error, instead of calling its Observer's + * onError closure, it will instead call pass the return value of + * resumeFunction to the Observer's onNext method. + * + * You can use this to prevent errors from propagating or to supply fallback data should errors + * be encountered. + *

          + * + * + * @param resumeFunction + * @return the original Observable with appropriately modified behavior + */ + public Observable onErrorReturn(Func1 resumeFunction) { + return onErrorReturn(this, resumeFunction); + } + + /** + * Instruct a Observable to emit a particular item rather than calling onError if + * it encounters an error. + * + * By default, when a Observable encounters an error that prevents it from emitting the expected + * object to its Observer, the Observable calls its Observer's onError closure, and + * then quits without calling any more of its Observer's closures. The + * onErrorReturn method changes this behavior. If you pass a closure + * (resumeFunction) to a Observable's onErrorReturn method, if the + * original Observable encounters an error, instead of calling its Observer's + * onError closure, it will instead call pass the return value of + * resumeFunction to the Observer's onNext method. + * + * You can use this to prevent errors from propagating or to supply fallback data should errors + * be encountered. + *

          + * + * + * @param that + * @param resumeFunction + * @return the original Observable with appropriately modified behavior + */ + public Observable onErrorReturn(final Object resumeFunction) { + return onErrorReturn(this, new Func1() { + + public T call(Exception e) { + return Functions.execute(resumeFunction, e); + } + }); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the final result from the final call to your closure as its sole + * output. + *

          + * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," + * "compress," or "inject" in other programming contexts. Groovy, for instance, has an + * inject method that does a similar operation on lists. + *

          + * + * + * @param accumulator + * An accumulator closure to be invoked on each element from the sequence, whose result + * will be used in the next accumulator call (if applicable). + * + * @return An observable sequence with a single element from the result of accumulating the + * output from the list of Observables. + * @see MSDN: Observable.Aggregate + * @see Wikipedia: Fold (higher-order function) + */ + public Observable reduce(Func2 accumulator) { + return reduce(this, accumulator); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the final result from the final call to your closure as its sole + * output. + *

          + * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," + * "compress," or "inject" in other programming contexts. Groovy, for instance, has an + * inject method that does a similar operation on lists. + *

          + * + * + * @param accumulator + * An accumulator closure to be invoked on each element from the sequence, whose result + * will be used in the next accumulator call (if applicable). + * + * @return A Observable that emits a single element from the result of accumulating the output + * from the list of Observables. + * @see MSDN: Observable.Aggregate + * @see Wikipedia: Fold (higher-order function) + */ + public Observable reduce(Object accumulator) { + return reduce(this, accumulator); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the final result from the final call to your closure as its sole + * output. + *

          + * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," + * "compress," or "inject" in other programming contexts. Groovy, for instance, has an + * inject method that does a similar operation on lists. + *

          + * + * + * @param initialValue + * The initial (seed) accumulator value. + * @param accumulator + * An accumulator closure to be invoked on each element from the sequence, whose + * result will be used in the next accumulator call (if applicable). + * + * @return A Observable that emits a single element from the result of accumulating the output + * from the list of Observables. + * @see MSDN: Observable.Aggregate + * @see Wikipedia: Fold (higher-order function) + */ + public Observable reduce(T initialValue, Func2 accumulator) { + return reduce(this, initialValue, accumulator); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the final result from the final call to your closure as its sole + * output. + *

          + * This technique, which is called "reduce" here, is sometimes called "fold," "accumulate," + * "compress," or "inject" in other programming contexts. Groovy, for instance, has an + * inject method that does a similar operation on lists. + *

          + * + * + * @param initialValue + * The initial (seed) accumulator value. + * @param accumulator + * An accumulator closure to be invoked on each element from the sequence, whose + * result will be used in the next accumulator call (if applicable). + * @return A Observable that emits a single element from the result of accumulating the output + * from the list of Observables. + * @see MSDN: Observable.Aggregate + * @see Wikipedia: Fold (higher-order function) + */ + public Observable reduce(T initialValue, Object accumulator) { + return reduce(this, initialValue, accumulator); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the result of each of these iterations. It emits the result of + * each of these iterations as a sequence from the returned Observable. This sort of closure is + * sometimes called an accumulator. + *

          + * + * + * @param accumulator + * An accumulator closure to be invoked on each element from the sequence whose + * result will be sent via onNext and used in the next accumulator call + * (if applicable). + * @return A Observable sequence whose elements are the result of accumulating the output from + * the list of Observables. + * @see MSDN: Observable.Scan + */ + public Observable scan(Func2 accumulator) { + return scan(this, accumulator); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the result of each of these iterations. It emits the result of + * each of these iterations as a sequence from the returned Observable. This sort of closure is + * sometimes called an accumulator. + *

          + * + * + * @param accumulator + * An accumulator closure to be invoked on each element from the sequence whose + * result will be sent via onNext and used in the next accumulator call + * (if applicable). + * + * @return A Observable sequence whose elements are the result of accumulating the output from + * the list of Observables. + * @see MSDN: Observable.Scan + */ + public Observable scan(final Object accumulator) { + return scan(this, accumulator); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, and so on until all items have been emitted by the + * source Observable, emitting the result of each of these iterations. This sort of closure is + * sometimes called an accumulator. + *

          + * + * + * @param initialValue + * The initial (seed) accumulator value. + * @param accumulator + * An accumulator closure to be invoked on each element from the sequence whose + * result will be sent via onNext and used in the next accumulator call + * (if applicable). + * @return A Observable sequence whose elements are the result of accumulating the output from + * the list of Observables. + * @see MSDN: Observable.Scan + */ + public Observable scan(T initialValue, Func2 accumulator) { + return scan(this, initialValue, accumulator); + } + + /** + * Returns a Observable that applies a closure of your choosing to the first item emitted by a + * source Observable, then feeds the result of that closure along with the second item emitted + * by a Observable into the same closure, then feeds the result of that closure along with the + * third item into the same closure, and so on, emitting the result of each of these + * iterations. This sort of closure is sometimes called an accumulator. + *

          + * + * + * @param initialValue + * The initial (seed) accumulator value. + * @param accumulator + * An accumulator closure to be invoked on each element from the sequence whose result + * will be sent via onNext and used in the next accumulator call (if + * applicable). + * @return A Observable sequence whose elements are the result of accumulating the output from + * the list of Observables. + * @see MSDN: Observable.Scan + */ + public Observable scan(final T initialValue, final Object accumulator) { + return scan(this, initialValue, accumulator); + } + + /** + * Returns a Observable that skips the first num items emitted by the source + * Observable. + * You can ignore the first num items emitted by a Observable and attend only to + * those items that come after, by modifying the Observable with the skip method. + *

          + * + * + * @param num + * The number of items to skip + * @return A Observable sequence that is identical to the source Observable except that it does + * not emit the first num items from that sequence. + */ + public Observable skip(int num) { + return skip(this, num); + } + + /** + * Returns a Observable that emits the first num items emitted by the source + * Observable. + * + * You can choose to pay attention only to the first num values emitted by a + * Observable by calling its take method. This method returns a Observable that will + * call a subscribing Observer's onNext closure a maximum of num times + * before calling onCompleted. + *

          + * + * + * @param num + * @return a Observable that emits only the first num items from the source + * Observable, or all of the items from the source Observable if that Observable emits + * fewer than num items. + */ + public Observable take(final int num) { + return take(this, num); + } + + /** + * Returns a Observable that emits a single item, a list composed of all the items emitted by + * the source Observable. + * + * Normally, a Observable that returns multiple items will do so by calling its Observer's + * onNext closure for each such item. You can change this behavior, instructing + * the Observable to compose a list of all of these multiple items and then to call the + * Observer's onNext closure once, passing it the entire list, by calling the + * Observable object's toList method prior to calling its subscribe + * method. + *

          + * + * + * @return a Observable that emits a single item: a List containing all of the items emitted by + * the source Observable. + */ + public Observable> toList() { + return toList(this); + } + + /** + * Sort T objects by their natural order (object must implement Comparable). + *

          + * + * + * @throws ClassCastException + * if T objects do not implement Comparable + * @return + */ + public Observable> toSortedList() { + return toSortedList(this); + } + + /** + * Sort T objects using the defined sort function. + *

          + * + * + * @param sortFunction + * @return + */ + public Observable> toSortedList(Func2 sortFunction) { + return toSortedList(this, sortFunction); + } + + /** + * Sort T objects using the defined sort function. + *

          + * + * + * @param sortFunction + * @return + */ + public Observable> toSortedList(final Object sortFunction) { + return toSortedList(this, sortFunction); + } + + public static class UnitTest { + private static interface ScriptAssertion { + public void error(Exception o); + + public void received(Object o); + } + + private static class TestFactory { + int counter = 1; + + @SuppressWarnings("unused") + public Observable getNumbers() { + return toObservable(1, 3, 2, 5, 4); + } + + @SuppressWarnings("unused") + public TestObservable getObservable() { + return new TestObservable(counter++); + } + } + + private static class TestObservable extends Observable { + private final int count; + + public TestObservable(int count) { + this.count = count; + } + + public Subscription subscribe(Observer observer) { + + observer.onNext("hello_" + count); + observer.onCompleted(); + + return new Subscription() { + + public void unsubscribe() { + // unregister ... will never be called here since we are executing synchronously + } + + }; + } + } + + @Mock + ScriptAssertion assertion; + + @Mock + Observer w; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + private void runGroovyScript(String script) { + ClassLoader parent = getClass().getClassLoader(); + @SuppressWarnings("resource") + GroovyClassLoader loader = new GroovyClassLoader(parent); + + Binding binding = new Binding(); + binding.setVariable("mockApiCall", new TestFactory()); + binding.setVariable("a", assertion); + binding.setVariable("o", org.rx.reactive.Observable.class); + + /* parse the script and execute it */ + InvokerHelper.createScript(loader.parseClass(script), binding).run(); + } + + @Test + public void testCreateViaGroovy() { + runGroovyScript("o.create({it.onNext('hello');it.onCompleted();}).subscribe({ result -> a.received(result)});"); + verify(assertion, times(1)).received("hello"); + } + + @Test + public void testFilterViaGroovy() { + runGroovyScript("o.filter(o.toObservable(1, 2, 3), {it >= 2}).subscribe({ result -> a.received(result)});"); + verify(assertion, times(0)).received(1); + verify(assertion, times(1)).received(2); + verify(assertion, times(1)).received(3); + } + + @Test + public void testLast() { + String script = "mockApiCall.getObservable().last().subscribe({ result -> a.received(result)});"; + runGroovyScript(script); + verify(assertion, times(1)).received("hello_1"); + } + + @Test + public void testMap() { + String script = "mockApiCall.getObservable().map({v -> 'say' + v}).subscribe({ result -> a.received(result)});"; + runGroovyScript(script); + verify(assertion, times(1)).received("sayhello_1"); + } + + @Test + public void testMapViaGroovy() { + runGroovyScript("o.map(o.toObservable(1, 2, 3), {'hello_' + it}).subscribe({ result -> a.received(result)});"); + verify(assertion, times(1)).received("hello_" + 1); + verify(assertion, times(1)).received("hello_" + 2); + verify(assertion, times(1)).received("hello_" + 3); + } + + @Test + public void testMaterializeViaGroovy() { + runGroovyScript("o.materialize(o.toObservable(1, 2, 3)).subscribe({ result -> a.received(result)});"); + // we expect 4 onNext calls: 3 for 1, 2, 3 ObservableNotification.OnNext and 1 for ObservableNotification.OnCompleted + verify(assertion, times(4)).received(any(Notification.class)); + verify(assertion, times(0)).error(any(Exception.class)); + } + + @Test + public void testMergeDelayErrorViaGroovy() { + runGroovyScript("o.mergeDelayError(o.toObservable(1, 2, 3), o.merge(o.toObservable(6), o.error(new NullPointerException()), o.toObservable(7)), o.toObservable(4, 5)).subscribe({ result -> a.received(result)}, { exception -> a.error(exception)});"); + verify(assertion, times(1)).received(1); + verify(assertion, times(1)).received(2); + verify(assertion, times(1)).received(3); + verify(assertion, times(1)).received(4); + verify(assertion, times(1)).received(5); + verify(assertion, times(1)).received(6); + verify(assertion, times(0)).received(7); + verify(assertion, times(1)).error(any(NullPointerException.class)); + } + + @Test + public void testMergeViaGroovy() { + runGroovyScript("o.merge(o.toObservable(1, 2, 3), o.merge(o.toObservable(6), o.error(new NullPointerException()), o.toObservable(7)), o.toObservable(4, 5)).subscribe({ result -> a.received(result)}, { exception -> a.error(exception)});"); + // executing synchronously so we can deterministically know what order things will come + verify(assertion, times(1)).received(1); + verify(assertion, times(1)).received(2); + verify(assertion, times(1)).received(3); + verify(assertion, times(0)).received(4); // the NPE will cause this sequence to be skipped + verify(assertion, times(0)).received(5); // the NPE will cause this sequence to be skipped + verify(assertion, times(1)).received(6); // this comes before the NPE so should exist + verify(assertion, times(0)).received(7);// this comes in the sequence after the NPE + verify(assertion, times(1)).error(any(NullPointerException.class)); + } + + @Test + public void testReduce() { + Observable Observable = toObservable(1, 2, 3, 4); + reduce(Observable, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + + }).subscribe(w); + // we should be called only once + verify(w, times(1)).onNext(anyInt()); + verify(w).onNext(10); + } + + @Test + public void testReduceWithInitialValue() { + Observable Observable = toObservable(1, 2, 3, 4); + reduce(Observable, 50, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + + }).subscribe(w); + // we should be called only once + verify(w, times(1)).onNext(anyInt()); + verify(w).onNext(60); + } + + @Test + public void testScriptWithMaterialize() { + String script = "mockApiCall.getObservable().materialize().subscribe({ result -> a.received(result)});"; + runGroovyScript(script); + // 2 times: once for hello_1 and once for onCompleted + verify(assertion, times(2)).received(any(Notification.class)); + } + + @Test + public void testScriptWithMerge() { + String script = "o.merge(mockApiCall.getObservable(), mockApiCall.getObservable()).subscribe({ result -> a.received(result)});"; + runGroovyScript(script); + verify(assertion, times(1)).received("hello_1"); + verify(assertion, times(1)).received("hello_2"); + } + + @Test + public void testScriptWithOnNext() { + String script = "mockApiCall.getObservable().subscribe({ result -> a.received(result)})"; + runGroovyScript(script); + verify(assertion).received("hello_1"); + } + + @Test + public void testSkipTakeViaGroovy() { + runGroovyScript("o.skip(o.toObservable(1, 2, 3), 1).take(1).subscribe({ result -> a.received(result)});"); + verify(assertion, times(0)).received(1); + verify(assertion, times(1)).received(2); + verify(assertion, times(0)).received(3); + } + + @Test + public void testSkipViaGroovy() { + runGroovyScript("o.skip(o.toObservable(1, 2, 3), 2).subscribe({ result -> a.received(result)});"); + verify(assertion, times(0)).received(1); + verify(assertion, times(0)).received(2); + verify(assertion, times(1)).received(3); + } + + @Test + public void testTakeViaGroovy() { + runGroovyScript("o.take(o.toObservable(1, 2, 3), 2).subscribe({ result -> a.received(result)});"); + verify(assertion, times(1)).received(1); + verify(assertion, times(1)).received(2); + verify(assertion, times(0)).received(3); + } + + @Test + public void testToSortedList() { + runGroovyScript("mockApiCall.getNumbers().toSortedList().subscribe({ result -> a.received(result)});"); + verify(assertion, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); + } + + @Test + public void testToSortedListStatic() { + runGroovyScript("o.toSortedList(o.toObservable(1, 3, 2, 5, 4)).subscribe({ result -> a.received(result)});"); + verify(assertion, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); + } + + @Test + public void testToSortedListWithFunction() { + runGroovyScript("mockApiCall.getNumbers().toSortedList({a, b -> a - b}).subscribe({ result -> a.received(result)});"); + verify(assertion, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); + } + + @Test + public void testToSortedListWithFunctionStatic() { + runGroovyScript("o.toSortedList(o.toObservable(1, 3, 2, 5, 4), {a, b -> a - b}).subscribe({ result -> a.received(result)});"); + verify(assertion, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); + } + } +} diff --git a/rxjava-core/src/main/java/org/rx/reactive/Observer.java b/rxjava-core/src/main/java/org/rx/reactive/Observer.java new file mode 100644 index 0000000000..eca51c7e71 --- /dev/null +++ b/rxjava-core/src/main/java/org/rx/reactive/Observer.java @@ -0,0 +1,46 @@ +package org.rx.reactive; + +/** + * Provides a mechanism for receiving push-based notifications. + *

          + * After an Observer calls a Observable's Observable.subscribe method, the Observable calls the Observer's onNext method to provide notifications. A well-behaved Observable will + * call a Observer's onCompleted closure exactly once or the Observer's onError closure exactly once. + *

          + * For more informationon, see: API.Next + * Programmer's Guide: Observers, Observables, and the Reactive Pattern: Setting up Observers in Groovy + * + * @param + */ +public interface Observer { + + /** + * Notifies the Observer that the Observable has finished sending push-based notifications. + *

          + * The Observable will not call this closure if it calls onError. + */ + public void onCompleted(); + + /** + * Notifies the Observer that the Observable has experienced an error condition. + *

          + * If the Observable calls this closure, it will not thereafter call onNext or onCompleted. + * + * @param e + */ + public void onError(Exception e); + + /** + * Provides the Observer with new data. + *

          + * The Observable calls this closure 1 or more times, unless it calls onError in which case this closure may never be called. + *

          + * The Observable will not call this closure again after it calls either onCompleted or onError, though this does not guarantee that chronologically-speaking, this closure + * will not be called after one of those closures is called (because the Observable may assign the calling of these closures to chronologically-independent threads). See wx.synchronize() for information on how to enforce chronologically-ordered behavior. + * + * @param args + */ + public void onNext(T args); +} diff --git a/rxjava-core/src/main/java/org/rx/reactive/IDisposable.java b/rxjava-core/src/main/java/org/rx/reactive/Subscription.java similarity index 55% rename from rxjava-core/src/main/java/org/rx/reactive/IDisposable.java rename to rxjava-core/src/main/java/org/rx/reactive/Subscription.java index 356f5b6ea9..659e34396c 100644 --- a/rxjava-core/src/main/java/org/rx/reactive/IDisposable.java +++ b/rxjava-core/src/main/java/org/rx/reactive/Subscription.java @@ -1,11 +1,11 @@ package org.rx.reactive; -public interface IDisposable { +public interface Subscription { /** * Stop receiving notifications on the observer that was registered when this IDisposable was received. *

          - * This allows unregistering a watcher before it has finished receiving all events (ie. before onCompleted is called). + * This allows unregistering a Observer before it has finished receiving all events (ie. before onCompleted is called). */ public void unsubscribe(); diff --git a/rxjava-core/src/main/java/org/rx/reactive/package.html b/rxjava-core/src/main/java/org/rx/reactive/package.html index df8f6f043b..dd99e9f8dd 100644 --- a/rxjava-core/src/main/java/org/rx/reactive/package.html +++ b/rxjava-core/src/main/java/org/rx/reactive/package.html @@ -1,7 +1,7 @@

          A library that enables subscribing to and composing asynchronous events and callbacks.

          -

          The Watchable/Watcher interfaces and associated operators (in +

          The Observable/Observer interfaces and associated operators (in the .operations package) are inspired by and attempt to conform to the Reactive Rx library in Microsoft .Net.

          @@ -12,10 +12,10 @@

          Compared with the Microsoft implementation:

            -
          • Watchable == IObservable
          • -
          • Watcher == IObserver
          • -
          • WatchableSubscription == IDisposable
          • -
          • WatchableExtensions == Observable
          • +
          • Observable == IObservable
          • +
          • Observer == IObserver
          • +
          • Subscription == IDisposable
          • +
          • ObservableExtensions == Observable

          Services which intend on exposing data asynchronously and wish