From 62564d6641746dbd3bfaa5f82a69dbf76c2418d3 Mon Sep 17 00:00:00 2001 From: headinthebox Date: Tue, 19 Nov 2013 16:58:49 -0800 Subject: [PATCH 1/3] Scala Bindings Refactor --- .../rx/lang/scala/examples/MovieLibUsage.java | 34 +- .../lang/scala/examples => }/MovieLib.scala | 12 +- .../lang/scala/examples => }/Olympics.scala | 16 +- .../ImplicitFunctionConversions.scala | 32 +- .../{rx/lang/scala => }/Notification.scala | 19 +- .../{rx/lang/scala => }/Observable.scala | 1092 ++++++++--------- .../src/main/scala/Observer.scala | 47 + .../src/main/scala/Scheduler.scala | 212 ++++ .../src/main/scala/WithFilter.scala | 23 + .../scala => }/concurrency/Schedulers.scala | 15 - .../scala/concurrency/TestScheduler.scala | 105 ++ .../observables/BlockingObservable.scala | 10 +- .../main/scala/rx/lang/scala/Scheduler.scala | 176 --- .../scala/concurrency/TestScheduler.scala | 120 -- .../rx/lang/scala/concurrency/package.scala | 31 - .../rx/lang/scala/observables/package.scala | 27 - .../main/scala/rx/lang/scala/package.scala | 158 --- .../rx/lang/scala/subjects/package.scala | 46 - .../main/scala/subjects/AsyncSubject.scala | 11 + .../main/scala/subjects/BehaviorSubject.scala | 15 + .../main/scala/subjects/PublishSubject.scala | 12 + .../main/scala/subjects/ReplaySubject.scala | 15 + .../src/main/scala/subjects/Subject.scala | 11 + .../subscriptions/BooleanSubscription.scala | 38 + .../subscriptions/CompositeSubscription.scala | 61 + .../MultiAssignmentSubscription.scala | 54 + .../subscriptions/SerialSubscription.scala | 48 + .../scala/subscriptions/Subscription.scala | 83 ++ .../src/main/scala/subscriptions/scala.scala | 24 + .../util/package.scala => util/util.scala} | 10 +- .../scala/RxJavaDemos.scala} | 208 ++-- .../src/test/scala/SubscriptionTests.scala | 114 ++ .../src/test/scala/UnitTestSuite.scala | 87 ++ .../rx/lang/scala/CompletenessTest.scala | 369 ------ 34 files changed, 1626 insertions(+), 1709 deletions(-) rename language-adaptors/rxjava-scala/src/examples/scala/{rx/lang/scala/examples => }/MovieLib.scala (97%) rename language-adaptors/rxjava-scala/src/examples/scala/{rx/lang/scala/examples => }/Olympics.scala (98%) rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala => }/ImplicitFunctionConversions.scala (80%) rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala => }/Notification.scala (70%) rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala => }/Observable.scala (73%) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/Observer.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala => }/concurrency/Schedulers.scala (80%) create mode 100644 language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala => }/observables/BlockingObservable.scala (95%) delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala delete mode 100644 language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala create mode 100644 language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala rename language-adaptors/rxjava-scala/src/main/scala/{rx/lang/scala/util/package.scala => util/util.scala} (67%) rename language-adaptors/rxjava-scala/src/{examples/scala/rx/lang/scala/examples/RxScalaDemo.scala => test/scala/RxJavaDemos.scala} (91%) create mode 100644 language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala create mode 100644 language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala delete mode 100644 language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala diff --git a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java index 84920e0d12..19a618d158 100644 --- a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java +++ b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java @@ -15,25 +15,27 @@ */ package rx.lang.scala.examples; -import org.junit.Test; - import rx.Observable; -import rx.util.functions.Action1; +import rx.lang.scala.examples.Movie; +import rx.lang.scala.examples.MovieLib; +import static rx.lang.scala.ImplicitFunctionConversions.toScalaObservable; public class MovieLibUsage { - - Action1 moviePrinter = new Action1() { - public void call(Movie m) { - System.out.println("A movie of length " + m.lengthInSeconds() + "s"); - } - }; - - @Test - public void test() { - MovieLib lib = new MovieLib(Observable.from(new Movie(3000), new Movie(1000), new Movie(2000))); - - lib.longMovies().subscribe(moviePrinter); - } + + public static void main(String[] args) { + + Observable movies = Observable.from( + new Movie(3000), + new Movie(1000), + new Movie(2000) + ); + + MovieLib lib = new MovieLib(toScalaObservable(movies)); + + lib.longMovies().asJavaObservable().subscribe(m -> + System.out.println("A movie of length " + m.lengthInSeconds() + "s") + ); + } } diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala b/language-adaptors/rxjava-scala/src/examples/scala/MovieLib.scala similarity index 97% rename from language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala rename to language-adaptors/rxjava-scala/src/examples/scala/MovieLib.scala index 1eefac79c9..8e2ce5b29b 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/MovieLib.scala @@ -1,12 +1,12 @@ /** * Copyright 2013 Netflix, Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,11 +20,11 @@ import rx.lang.scala.Observable class Movie(val lengthInSeconds: Int) { } class MovieLib(val moviesStream: Observable[Movie]) { - + val threshold = 1200 - + def shortMovies: Observable[Movie] = moviesStream.filter(_.lengthInSeconds <= threshold) - + def longMovies: Observable[Movie] = moviesStream.filter(_.lengthInSeconds > threshold) } diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala b/language-adaptors/rxjava-scala/src/examples/scala/Olympics.scala similarity index 98% rename from language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala rename to language-adaptors/rxjava-scala/src/examples/scala/Olympics.scala index 699523ea55..7a11bdf539 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala +++ b/language-adaptors/rxjava-scala/src/examples/scala/Olympics.scala @@ -20,7 +20,7 @@ import scala.concurrent.duration._ object Olympics { case class Medal(val year: Int, val games: String, val discipline: String, val medal: String, val athlete: String, val country: String) - + def mountainBikeMedals: Observable[Medal] = Observable( Observable( Medal(1996, "Atlanta 1996", "cross-country men", "Gold", "Bart BRENTJENS", "Netherlands"), @@ -31,7 +31,7 @@ object Olympics { Medal(1996, "Atlanta 1996", "cross-country women", "Bronze", "Susan DEMATTEI", "United States of America") ), fourYearsEmpty, - Observable( + Observable( Medal(2000, "Sydney 2000", "cross-country women", "Gold", "Paola PEZZO", "Italy"), Medal(2000, "Sydney 2000", "cross-country women", "Silver", "Barbara BLATTER", "Switzerland"), Medal(2000, "Sydney 2000", "cross-country women", "Bronze", "Marga FULLANA", "Spain"), @@ -40,7 +40,7 @@ object Olympics { Medal(2000, "Sydney 2000", "cross-country men", "Bronze", "Christoph SAUSER", "Switzerland") ), fourYearsEmpty, - Observable( + Observable( Medal(2004, "Athens 2004", "cross-country men", "Gold", "Julien ABSALON", "France"), Medal(2004, "Athens 2004", "cross-country men", "Silver", "Jose Antonio HERMIDA RAMOS", "Spain"), Medal(2004, "Athens 2004", "cross-country men", "Bronze", "Bart BRENTJENS", "Netherlands"), @@ -49,7 +49,7 @@ object Olympics { Medal(2004, "Athens 2004", "cross-country women", "Bronze", "Sabine SPITZ", "Germany") ), fourYearsEmpty, - Observable( + Observable( Medal(2008, "Beijing 2008", "cross-country women", "Gold", "Sabine SPITZ", "Germany"), Medal(2008, "Beijing 2008", "cross-country women", "Silver", "Maja WLOSZCZOWSKA", "Poland"), Medal(2008, "Beijing 2008", "cross-country women", "Bronze", "Irina KALENTYEVA", "Russian Federation"), @@ -67,12 +67,12 @@ object Olympics { Medal(2012, "London 2012", "cross-country women", "Bronze", "Georgia GOULD", "United States of America") ) ).concat - + // speed it up :D val fourYears = 4000.millis - + val neverUsedDummyMedal = Medal(3333, "?", "?", "?", "?", "?") - + def fourYearsEmpty: Observable[Medal] = { // TODO this should return an observable which emits nothing during fourYears and then completes // Because of https://github.com/Netflix/RxJava/issues/388, we get non-terminating tests @@ -82,5 +82,5 @@ object Olympics { // But we just return empty, which completes immediately Observable() } - + } \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala similarity index 80% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala rename to language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala index 5db1c673f6..67f24af638 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala @@ -15,8 +15,12 @@ */ package rx.lang.scala +import java.lang.Exception import java.{ lang => jlang } +import rx.lang.scala._ import rx.util.functions._ +import scala.collection.Seq +import rx.lang.scala.subscriptions.Subscription /** * These function conversions convert between Scala functions and Rx `Func`s and `Action`s. @@ -28,20 +32,28 @@ object ImplicitFunctionConversions { import language.implicitConversions implicit def schedulerActionToFunc2[T](action: (Scheduler, T) => Subscription) = - new Func2[rx.Scheduler, T, Subscription] { - def call(s: rx.Scheduler, t: T): Subscription = { - action(s, t) + new Func2[rx.Scheduler, T, rx.Subscription] { + def call(s: rx.Scheduler, t: T): rx.Subscription = { + action(s, t).asJavaSubscription } - } - - implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = s.asJava - + } + + implicit def toJavaSubscription(s: Subscription): rx.Subscription = s.asJavaSubscription + implicit def toScalaSubscription(s: rx.Subscription): Subscription = Subscription(s) + + implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = s.asJavaScheduler implicit def javaSchedulerToScalaScheduler(s: rx.Scheduler): Scheduler = Scheduler(s) + + implicit def toJavaObserver[T](s: Observer[T]): rx.Observer[_ >: T] = s.asJavaObserver + implicit def toScalaObserver[T](s: rx.Observer[T]): Observer[T] = Observer(s) + + implicit def toJavaObservable[T](s: Observable[T]): rx.Observable[_ <: T] = s.asJavaObservable + implicit def toScalaObservable[T](s: rx.Observable[T]): Observable[T] = Observable(s) implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) = new rx.Observable.OnSubscribeFunc[T] { def onSubscribe(obs: rx.Observer[_ >: T]): rx.Subscription = { - f(obs) + f(Observer(obs)) } } @@ -55,6 +67,10 @@ object ImplicitFunctionConversions { def call(): Unit = f() } + implicit def Action1toScalaFunction1ProducingUnit[A](f: Action1[A]): (A=>Unit) = { + a => f(a) + } + implicit def scalaFunction1ProducingUnitToAction1[A](f: (A => Unit)): Action1[A] = new Action1[A] { def call(a: A): Unit = f(a) diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/Notification.scala similarity index 70% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala rename to language-adaptors/rxjava-scala/src/main/scala/Notification.scala index 133157d5ca..430cfd8e80 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/Notification.scala @@ -1,22 +1,7 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package rx.lang.scala /** - * Emitted by Observables returned by [[Observable.materialize]]. + * Emitted by Observables returned by [[rx.lang.scala.Observable.materialize]]. */ sealed trait Notification[+T] { def asJava: rx.Notification[_ <: T] @@ -58,7 +43,7 @@ object Notification { } class OnError[+T](val asJava: rx.Notification[_ <: T]) extends Notification[T] { - def error: Throwable = asJava.getThrowable() + def error: Throwable = asJava.getThrowable } object OnError { diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/Observable.scala similarity index 73% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala rename to language-adaptors/rxjava-scala/src/main/scala/Observable.scala index 29111f4cf7..a0dfaf3601 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/Observable.scala @@ -16,22 +16,27 @@ package rx.lang.scala + +import rx.util.functions.FuncN +import rx.Observable.OnSubscribeFunc + + /** * The Observable interface that implements the Reactive Pattern. - * - * @param asJava the underlying Java observable - * + * + * @param asJavaObservable the underlying Java observable + * * @define subscribeObserverMain - * Call this method to subscribe an [[Observer]] for receiving + * Call this method to subscribe an [[rx.lang.scala.Observer]] for receiving * items and notifications from the Observable. - * + * * A typical implementation of `subscribe` does the following: * * It stores a reference to the Observer in a collection object, such as a `List[T]` object. * - * It returns a reference to the [[Subscription]] interface. This enables Observers to + * It returns a reference to the [[rx.lang.scala.Subscription]] interface. This enables Observers to * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's [[Observer.onCompleted onCompleted]] method. + * sending them, which also invokes the Observer's [[rx.lang.scala.Observer.onCompleted onCompleted]] method. * * An `Observable[T]` instance is responsible for accepting all subscriptions * and notifying all Observers. Unless the documentation for a particular @@ -41,17 +46,17 @@ package rx.lang.scala * @define subscribeObserverParamObserver * the observer * @define subscribeObserverParamScheduler - * the [[Scheduler]] on which Observers subscribe to the Observable + * the [[rx.lang.scala.Scheduler]] on which Observers subscribe to the Observable * @define subscribeAllReturn - * a [[Subscription]] reference whose `unsubscribe` method can be called to stop receiving items + * a [[rx.lang.scala.Subscription]] reference whose `unsubscribe` method can be called to stop receiving items * before the Observable has finished sending them - * + * * @define subscribeCallbacksMainWithNotifications * Call this method to receive items and notifications from this observable. - * + * * @define subscribeCallbacksMainNoNotifications * Call this method to receive items from this observable. - * + * * @define subscribeCallbacksParamOnNext * this function will be called whenever the Observable emits an item * @define subscribeCallbacksParamOnError @@ -67,64 +72,62 @@ package rx.lang.scala * - [[http://unscriptable.com/2009/03/20/debouncing-javascript-methods/]] * - [[http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/]] * - * + * */ -// constructor is private because users should use apply in companion -class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) - // Uncommenting this line combined with `new Observable(...)` instead of `new Observable[T](...)` - // makes the compiler crash - extends AnyVal +trait Observable[+T] { - import scala.collection.JavaConverters._ import scala.collection.Seq import scala.concurrent.duration.{Duration, TimeUnit} - import rx.{Observable => JObservable} import rx.util.functions._ import rx.lang.scala.util._ - import rx.lang.scala.subjects.Subject import rx.lang.scala.observables.BlockingObservable import rx.lang.scala.ImplicitFunctionConversions._ - + + def asJavaObservable: rx.Observable[_ <: T] + /** * $subscribeObserverMain - * + * * @param observer $subscribeObserverParamObserver * @param scheduler $subscribeObserverParamScheduler * @return $subscribeAllReturn */ def subscribe(observer: Observer[T], scheduler: Scheduler): Subscription = { - asJava.subscribe(observer, scheduler) + asJavaObservable.subscribe(observer.asJavaObserver, scheduler) } /** * $subscribeObserverMain - * + * * @param observer $subscribeObserverParamObserver * @return $subscribeAllReturn */ def subscribe(observer: Observer[T]): Subscription = { - asJava.subscribe(observer) + asJavaObservable.subscribe(observer.asJavaObserver) } /** * $subscribeCallbacksMainNoNotifications - * + * `` * @param onNext $subscribeCallbacksParamOnNext * @return $subscribeAllReturn */ def subscribe(onNext: T => Unit): Subscription = { - asJava.subscribe(onNext) + asJavaObservable.subscribe(scalaFunction1ProducingUnitToAction1(onNext)) } - + /** * $subscribeCallbacksMainWithNotifications * * @param onNext $subscribeCallbacksParamOnNext * @param onError $subscribeCallbacksParamOnError * @return $subscribeAllReturn - */ + */ def subscribe(onNext: T => Unit, onError: Throwable => Unit): Subscription = { - asJava.subscribe(onNext, onError) + asJavaObservable.subscribe( + scalaFunction1ProducingUnitToAction1(onNext), + scalaFunction1ProducingUnitToAction1(onError) + ) } /** @@ -132,11 +135,15 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * @param onNext $subscribeCallbacksParamOnNext * @param onError $subscribeCallbacksParamOnError - * @param onComplete $subscribeCallbacksParamOnComplete + * @param onCompleted $subscribeCallbacksParamOnComplete * @return $subscribeAllReturn */ - def subscribe(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit): Subscription = { - asJava.subscribe(onNext, onError, onComplete) + def subscribe(onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit): Subscription = { + asJavaObservable.subscribe( + scalaFunction1ProducingUnitToAction1(onNext), + scalaFunction1ProducingUnitToAction1(onError), + scalaFunction0ProducingUnitToAction0(onCompleted) + ) } /** @@ -144,14 +151,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * @param onNext $subscribeCallbacksParamOnNext * @param onError $subscribeCallbacksParamOnError - * @param onComplete $subscribeCallbacksParamOnComplete + * @param onCompleted $subscribeCallbacksParamOnComplete * @param scheduler $subscribeCallbacksParamScheduler * @return $subscribeAllReturn */ - def subscribe(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit, scheduler: Scheduler): Subscription = { - asJava.subscribe(onNext, onError, onComplete, scheduler) + def subscribe(onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit, scheduler: Scheduler): Subscription = { + asJavaObservable.subscribe(scalaFunction1ProducingUnitToAction1(onNext), + scalaFunction1ProducingUnitToAction1(onError), + scalaFunction0ProducingUnitToAction0(onCompleted), + scheduler) } - + /** * $subscribeCallbacksMainWithNotifications * @@ -161,7 +171,10 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return $subscribeAllReturn */ def subscribe(onNext: T => Unit, onError: Throwable => Unit, scheduler: Scheduler): Subscription = { - asJava.subscribe(onNext, onError, scheduler) + asJavaObservable.subscribe( + scalaFunction1ProducingUnitToAction1(onNext), + scalaFunction1ProducingUnitToAction1(onError), + scheduler) } /** @@ -172,56 +185,56 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return $subscribeAllReturn */ def subscribe(onNext: T => Unit, scheduler: Scheduler): Subscription = { - asJava.subscribe(onNext, scheduler) + asJavaObservable.subscribe(scalaFunction1ProducingUnitToAction1(onNext), scheduler) } /** - * Returns a pair of a start function and an [[Observable]] that upon calling the start function causes the source Observable to + * Returns a pair of a start function and an [[rx.lang.scala.Observable]] that upon calling the start function causes the source Observable to * push results into the specified subject. - * + * * @param subject * the `rx.lang.scala.subjects.Subject` to push source items into * @tparam R * result type - * @return a pair of a start function and an [[Observable]] such that when the start function + * @return a pair of a start function and an [[rx.lang.scala.Observable]] such that when the start function * is called, the Observable starts to push results into the specified Subject */ - def multicast[R](subject: Subject[T, R]): (() => Subscription, Observable[R]) = { - val javaCO = asJava.multicast[R](subject) + def multicast[R](subject: rx.lang.scala.Subject[T, R]): (() => Subscription, Observable[R]) = { + val javaCO = asJavaObservable.multicast[R](subject.asJavaSubject) (() => javaCO.connect(), Observable[R](javaCO)) } - + /** * Returns an Observable that first emits the items emitted by `this`, and then the items emitted * by `that`. * * - * + * * @param that * an Observable to be appended * @return an Observable that emits items that are the result of combining the items emitted by * this and that, one after the other */ def ++[U >: T](that: Observable[U]): Observable[U] = { - val o1: JObservable[_ <: U] = this.asJava - val o2: JObservable[_ <: U] = that.asJava - Observable(JObservable.concat(o1, o2)) + val o1: rx.Observable[_ <: U] = this.asJavaObservable + val o2: rx.Observable[_ <: U] = that.asJavaObservable + Observable(rx.Observable.concat(o1, o2)) } /** * Returns an Observable that emits the items emitted by several Observables, one after the * other. - * + * * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, * otherwise you'll get a compilation error. - * + * * @usecase def concat[U]: Observable[U] * @inheritdoc */ def concat[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable val o5 = rx.Observable.concat[U](o4) Observable[U](o5) } @@ -232,31 +245,29 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * A well-behaved Observable does not interleave its invocations of the [[Observer.onNext onNext]], [[Observer.onCompleted onCompleted]], and [[Observer.onError onError]] methods of - * its [[Observer]]s; it invokes `onCompleted` or `onError` only once; and it never invokes `onNext` after invoking either `onCompleted` or `onError`. + * A well-behaved Observable does not interleave its invocations of the [[rx.lang.scala.Observer.onNext onNext]], [[rx.lang.scala.Observer.onCompleted onCompleted]], and [[rx.lang.scala.Observer.onError onError]] methods of + * its [[rx.lang.scala.Observer]]s; it invokes `onCompleted` or `onError` only once; and it never invokes `onNext` after invoking either `onCompleted` or `onError`. * `synchronize` enforces this, and the Observable it returns invokes `onNext` and `onCompleted` or `onError` synchronously. - * - * @param observable - * the source Observable + * * @return an Observable that is a chronologically well-behaved version of the source - * Observable, and that synchronously notifies its [[Observer]]s + * Observable, and that synchronously notifies its [[rx.lang.scala.Observer]]s */ def synchronize: Observable[T] = { - Observable[T](asJava.synchronize) + Observable[T](asJavaObservable.synchronize) } - + /** * Wraps each item emitted by a source Observable in a timestamped tuple. * * - * + * * @return an Observable that emits timestamped items from the source Observable */ def timestamp: Observable[(Long, T)] = { - Observable[rx.util.Timestamped[_ <: T]](asJava.timestamp()) - .map((t: rx.util.Timestamped[_ <: T]) => (t.getTimestampMillis, t.getValue())) + Observable[rx.util.Timestamped[_ <: T]](asJavaObservable.timestamp()) + .map((t: rx.util.Timestamped[_ <: T]) => (t.getTimestampMillis, t.getValue)) } - + /** * Returns an Observable formed from this Observable and another Observable by combining * corresponding elements in pairs. @@ -264,161 +275,161 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * is the minumum of the number of `onNext` invocations of `this` and `that`. */ def zip[U](that: Observable[U]): Observable[(T, U)] = { - Observable[(T, U)](JObservable.zip[T, U, (T, U)](this.asJava, that.asJava, (t: T, u: U) => (t, u))) + Observable[(T, U)](rx.Observable.zip[T, U, (T, U)](this.asJavaObservable, that.asJavaObservable, (t: T, u: U) => (t, u))) } /** * Zips this Observable with its indices. - * + * * @return An Observable emitting pairs consisting of all elements of this Observable paired with * their index. Indices start at 0. */ def zipWithIndex: Observable[(T, Int)] = { val fScala: (T, Integer) => (T, Int) = (elem: T, index: Integer) => (elem, index) val fJava : Func2[_ >: T, Integer, _ <: (T, Int)] = fScala - Observable[(T, Int)](asJava.mapWithIndex[(T, Int)](fJava)) + Observable[(T, Int)](asJavaObservable.mapWithIndex[(T, Int)](fJava)) } - + /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces connected non-overlapping buffers. The current buffer is * emitted and replaced with a new buffer when the Observable produced by the specified function produces a [[rx.lang.scala.util.Closing]] object. The function will then * be used to create a new Observable to listen for the end of the next buffer. - * + * * @param closings - * The function which is used to produce an [[Observable]] for every buffer created. - * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer + * The function which is used to produce an [[rx.lang.scala.Observable]] for every buffer created. + * When this [[rx.lang.scala.Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer * is emitted and replaced with a new one. * @return - * An [[Observable]] which produces connected non-overlapping buffers, which are emitted - * when the current [[Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers, which are emitted + * when the current [[rx.lang.scala.Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. */ def buffer(closings: () => Observable[Closing]) : Observable[Seq[T]] = { - val f: Func0[_ <: rx.Observable[_ <: Closing]] = closings().asJava - val jObs: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(f) + val f: Func0[_ <: rx.Observable[_ <: Closing]] = closings().asJavaObservable + val jObs: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(f) Observable.jObsOfListToScObsOfSeq(jObs.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces buffers. Buffers are created when the specified `openings` * Observable produces a [[rx.lang.scala.util.Opening]] object. Additionally the function argument * is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. When this * Observable produces such an object, the associated buffer is emitted. - * + * * @param openings - * The [[Observable]] which, when it produces a [[rx.lang.scala.util.Opening]] object, will cause + * The [[rx.lang.scala.Observable]] which, when it produces a [[rx.lang.scala.util.Opening]] object, will cause * another buffer to be created. * @param closings - * The function which is used to produce an [[Observable]] for every buffer created. - * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer + * The function which is used to produce an [[rx.lang.scala.Observable]] for every buffer created. + * When this [[rx.lang.scala.Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated buffer * is emitted. * @return - * An [[Observable]] which produces buffers which are created and emitted when the specified [[Observable]]s publish certain objects. + * An [[rx.lang.scala.Observable]] which produces buffers which are created and emitted when the specified [[rx.lang.scala.Observable]]s publish certain objects. */ def buffer(openings: Observable[Opening], closings: Opening => Observable[Closing]): Observable[Seq[T]] = { - val opening: rx.Observable[_ <: Opening] = openings.asJava - val closing: Func1[Opening, _ <: rx.Observable[_ <: Closing]] = (o: Opening) => closings(o).asJava - val jObs: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(opening, closing) + val opening: rx.Observable[_ <: Opening] = openings.asJavaObservable + val closing: Func1[Opening, _ <: rx.Observable[_ <: Closing]] = (o: Opening) => closings(o).asJavaObservable + val jObs: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(opening, closing) Observable.jObsOfListToScObsOfSeq(jObs.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces connected non-overlapping buffers, each containing `count` * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. - * + * * @param count * The maximum size of each buffer before it should be emitted. * @return - * An [[Observable]] which produces connected non-overlapping buffers containing at most + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers containing at most * `count` produced values. */ def buffer(count: Int): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(count) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(count) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces buffers every `skip` values, each containing `count` * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. - * + * * @param count * The maximum size of each buffer before it should be emitted. * @param skip * How many produced values need to be skipped before starting a new buffer. Note that when `skip` and * `count` are equals that this is the same operation as `buffer(int)`. * @return - * An [[Observable]] which produces buffers every `skip` values containing at most + * An [[rx.lang.scala.Observable]] which produces buffers every `skip` values containing at most * `count` produced values. */ def buffer(count: Int, skip: Int): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(count, skip) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(count, skip) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } - + /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces connected non-overlapping buffers, each of a fixed duration * specified by the `timespan` argument. When the source Observable completes or encounters * an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @return - * An [[Observable]] which produces connected non-overlapping buffers with a fixed duration. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers with a fixed duration. */ def buffer(timespan: Duration): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(timespan.length, timespan.unit) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } /** * Creates an Observable which produces buffers of collected values. - * + * * This Observable produces connected non-overlapping buffers, each of a fixed duration * specified by the `timespan` argument. When the source Observable completes or encounters * an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a buffer. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a buffer. * @return - * An [[Observable]] which produces connected non-overlapping buffers with a fixed duration. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers with a fixed duration. */ def buffer(timespan: Duration, scheduler: Scheduler): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(timespan.length, timespan.unit, scheduler) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit, scheduler) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } + } /** * Creates an Observable which produces buffers of collected values. This Observable produces connected * non-overlapping buffers, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @param count * The maximum size of each buffer before it should be emitted. * @return - * An [[Observable]] which produces connected non-overlapping buffers which are emitted after + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers which are emitted after * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). */ def buffer(timespan: Duration, count: Int): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(timespan.length, timespan.unit, count) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit, count) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } @@ -427,20 +438,20 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * non-overlapping buffers, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @param count * The maximum size of each buffer before it should be emitted. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a buffer. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a buffer. * @return - * An [[Observable]] which produces connected non-overlapping buffers which are emitted after + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers which are emitted after * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). */ def buffer(timespan: Duration, count: Int, scheduler: Scheduler): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(timespan.length, timespan.unit, count, scheduler) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit, count, scheduler) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) } @@ -449,46 +460,46 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * periodically, which is determined by the `timeshift` argument. Each buffer is emitted after a fixed timespan * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted. * @param timeshift * The period of time after which a new buffer will be created. * @return - * An [[Observable]] which produces new buffers periodically, and these are emitted after + * An [[rx.lang.scala.Observable]] which produces new buffers periodically, and these are emitted after * a fixed timespan has elapsed. */ def buffer(timespan: Duration, timeshift: Duration): Observable[Seq[T]] = { val span: Long = timespan.length val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) val unit: TimeUnit = timespan.unit - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(span, shift, unit) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(span, shift, unit) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - + } + /** * Creates an Observable which produces buffers of collected values. This Observable starts a new buffer * periodically, which is determined by the `timeshift` argument. Each buffer is emitted after a fixed timespan * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted. * @param timeshift * The period of time after which a new buffer will be created. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a buffer. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a buffer. * @return - * An [[Observable]] which produces new buffers periodically, and these are emitted after + * An [[rx.lang.scala.Observable]] which produces new buffers periodically, and these are emitted after * a fixed timespan has elapsed. */ def buffer(timespan: Duration, timeshift: Duration, scheduler: Scheduler): Observable[Seq[T]] = { val span: Long = timespan.length val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) val unit: TimeUnit = timespan.unit - val oJava: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(span, shift, unit, scheduler) + val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(span, shift, unit, scheduler) Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } + } /** * Creates an Observable which produces windows of collected values. This Observable produces connected @@ -496,19 +507,19 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Observable produced by the specified function produces a [[rx.lang.scala.util.Closing]] object. * The function will then be used to create a new Observable to listen for the end of the next * window. - * + * * @param closings - * The function which is used to produce an [[Observable]] for every window created. - * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window + * The function which is used to produce an [[rx.lang.scala.Observable]] for every window created. + * When this [[rx.lang.scala.Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window * is emitted and replaced with a new one. * @return - * An [[Observable]] which produces connected non-overlapping windows, which are emitted - * when the current [[Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows, which are emitted + * when the current [[rx.lang.scala.Observable]] created with the function argument produces a [[rx.lang.scala.util.Closing]] object. */ def window(closings: () => Observable[Closing]): Observable[Observable[T]] = { - val func : Func0[_ <: rx.Observable[_ <: Closing]] = closings().asJava - val o1: rx.Observable[_ <: rx.Observable[_]] = asJava.window(func) - val o2 = new Observable[rx.Observable[_]](o1).map((x: rx.Observable[_]) => { + val func : Func0[_ <: rx.Observable[_ <: Closing]] = closings().asJavaObservable + val o1: rx.Observable[_ <: rx.Observable[_]] = asJavaObservable.window(func) + val o2 = Observable[rx.Observable[_]](o1).map((x: rx.Observable[_]) => { val x2 = x.asInstanceOf[rx.Observable[_ <: T]] Observable[T](x2) }) @@ -520,207 +531,207 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Chunks are created when the specified `openings` Observable produces a [[rx.lang.scala.util.Opening]] object. * Additionally the `closings` argument is used to create an Observable which produces [[rx.lang.scala.util.Closing]] objects. * When this Observable produces such an object, the associated window is emitted. - * + * * @param openings - * The [[Observable]] which when it produces a [[rx.lang.scala.util.Opening]] object, will cause + * The [[rx.lang.scala.Observable]] which when it produces a [[rx.lang.scala.util.Opening]] object, will cause * another window to be created. * @param closings - * The function which is used to produce an [[Observable]] for every window created. - * When this [[Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window + * The function which is used to produce an [[rx.lang.scala.Observable]] for every window created. + * When this [[rx.lang.scala.Observable]] produces a [[rx.lang.scala.util.Closing]] object, the associated window * is emitted. * @return - * An [[Observable]] which produces windows which are created and emitted when the specified [[Observable]]s publish certain objects. + * An [[rx.lang.scala.Observable]] which produces windows which are created and emitted when the specified [[rx.lang.scala.Observable]]s publish certain objects. */ def window(openings: Observable[Opening], closings: Opening => Observable[Closing]) = { Observable.jObsOfJObsToScObsOfScObs( - asJava.window(openings.asJava, (op: Opening) => closings(op).asJava)) - : Observable[Observable[T]] // SI-7818 - } + asJavaObservable.window(openings.asJavaObservable, (op: Opening) => closings(op).asJavaObservable)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each containing `count` elements. When the source Observable completes or * encounters an error, the current window is emitted, and the event is propagated. - * + * * @param count * The maximum size of each window before it should be emitted. * @return - * An [[Observable]] which produces connected non-overlapping windows containing at most + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows containing at most * `count` produced values. */ def window(count: Int): Observable[Observable[T]] = { // this unnecessary ascription is needed because of this bug (without, compiler crashes): // https://issues.scala-lang.org/browse/SI-7818 - Observable.jObsOfJObsToScObsOfScObs(asJava.window(count)) : Observable[Observable[T]] + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(count)) : Observable[Observable[T]] } /** * Creates an Observable which produces windows of collected values. This Observable produces windows every * `skip` values, each containing `count` elements. When the source Observable completes or encounters an error, * the current window is emitted and the event is propagated. - * + * * @param count * The maximum size of each window before it should be emitted. * @param skip * How many produced values need to be skipped before starting a new window. Note that when `skip` and * `count` are equal that this is the same operation as `window(int)`. * @return - * An [[Observable]] which produces windows every `skip` values containing at most + * An [[rx.lang.scala.Observable]] which produces windows every `skip` values containing at most * `count` produced values. */ def window(count: Int, skip: Int): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJava.window(count, skip)) - : Observable[Observable[T]] // SI-7818 + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(count, skip)) + : Observable[Observable[T]] // SI-7818 } /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the `timespan` argument. When the source * Observable completes or encounters an error, the current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted, and * replaced with a new window. * @return - * An [[Observable]] which produces connected non-overlapping windows with a fixed duration. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows with a fixed duration. */ def window(timespan: Duration): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJava.window(timespan.length, timespan.unit)) - : Observable[Observable[T]] // SI-7818 - } + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the `timespan` argument. When the source * Observable completes or encounters an error, the current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted, and * replaced with a new window. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a window. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a window. * @return - * An [[Observable]] which produces connected non-overlapping windows with a fixed duration. + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows with a fixed duration. */ def window(timespan: Duration, scheduler: Scheduler): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJava.window(timespan.length, timespan.unit, scheduler)) - : Observable[Observable[T]] // SI-7818 - } + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit, scheduler)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted, and * replaced with a new window. * @param count * The maximum size of each window before it should be emitted. * @return - * An [[Observable]] which produces connected non-overlapping windows which are emitted after + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows which are emitted after * a fixed duration or when the window has reached maximum capacity (which ever occurs first). */ def window(timespan: Duration, count: Int): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJava.window(timespan.length, timespan.unit, count)) - : Observable[Observable[T]] // SI-7818 - } + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit, count)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable produces connected * non-overlapping windows, each of a fixed duration specified by the `timespan` argument or a maximum size * specified by the `count` argument (which ever is reached first). When the source Observable completes * or encounters an error, the current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted, and * replaced with a new window. * @param count * The maximum size of each window before it should be emitted. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a window. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a window. * @return - * An [[Observable]] which produces connected non-overlapping windows which are emitted after + * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows which are emitted after * a fixed duration or when the window has reached maximum capacity (which ever occurs first). */ def window(timespan: Duration, count: Int, scheduler: Scheduler): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJava.window(timespan.length, timespan.unit, count, scheduler)) - : Observable[Observable[T]] // SI-7818 - } + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit, count, scheduler)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable starts a new window * periodically, which is determined by the `timeshift` argument. Each window is emitted after a fixed timespan * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted. * @param timeshift * The period of time after which a new window will be created. * @return - * An [[Observable]] which produces new windows periodically, and these are emitted after + * An [[rx.lang.scala.Observable]] which produces new windows periodically, and these are emitted after * a fixed timespan has elapsed. */ def window(timespan: Duration, timeshift: Duration): Observable[Observable[T]] = { val span: Long = timespan.length val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) val unit: TimeUnit = timespan.unit - Observable.jObsOfJObsToScObsOfScObs(asJava.window(span, shift, unit)) - : Observable[Observable[T]] // SI-7818 - } + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(span, shift, unit)) + : Observable[Observable[T]] // SI-7818 + } /** * Creates an Observable which produces windows of collected values. This Observable starts a new window * periodically, which is determined by the `timeshift` argument. Each window is emitted after a fixed timespan * specified by the `timespan` argument. When the source Observable completes or encounters an error, the * current window is emitted and the event is propagated. - * + * * @param timespan * The period of time each window is collecting values before it should be emitted. * @param timeshift * The period of time after which a new window will be created. * @param scheduler - * The [[Scheduler]] to use when determining the end and start of a window. + * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a window. * @return - * An [[Observable]] which produces new windows periodically, and these are emitted after + * An [[rx.lang.scala.Observable]] which produces new windows periodically, and these are emitted after * a fixed timespan has elapsed. */ def window(timespan: Duration, timeshift: Duration, scheduler: Scheduler): Observable[Observable[T]] = { val span: Long = timespan.length val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) val unit: TimeUnit = timespan.unit - Observable.jObsOfJObsToScObsOfScObs(asJava.window(span, shift, unit, scheduler)) - : Observable[Observable[T]] // SI-7818 - } - + Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(span, shift, unit, scheduler)) + : Observable[Observable[T]] // SI-7818 + } + /** * Returns an Observable which only emits those items for which a given predicate holds. - * + * * - * + * * @param predicate * a function that evaluates the items emitted by the source Observable, returning `true` if they pass the filter * @return an Observable that emits only those items in the original Observable that the filter * evaluates as `true` */ def filter(predicate: T => Boolean): Observable[T] = { - Observable[T](asJava.filter(predicate)) + Observable[T](asJavaObservable.filter(predicate)) } /** - * Registers an function to be called when this Observable invokes [[Observer.onCompleted onCompleted]] or [[Observer.onError onError]]. + * Registers an function to be called when this Observable invokes [[rx.lang.scala.Observer.onCompleted onCompleted]] or [[rx.lang.scala.Observer.onError onError]]. * * - * + * * @param action * an function to be invoked when the source Observable finishes * @return an Observable that emits the same items as the source Observable, then invokes the function */ def finallyDo(action: () => Unit): Observable[T] = { - Observable[T](asJava.finallyDo(action)) - } + Observable[T](asJavaObservable.finallyDo(action)) + } /** * Creates a new Observable by applying a function that you supply to each item emitted by @@ -728,8 +739,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * resulting Observables and emitting the results of this merger. * * - * - * @param func + * + * @param f * a function that, when applied to an item emitted by the source Observable, returns * an Observable * @return an Observable that emits the result of applying the transformation function to each @@ -737,201 +748,206 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * obtained from this transformation. */ def flatMap[R](f: T => Observable[R]): Observable[R] = { - Observable[R](asJava.flatMap[R]((t: T) => f(t).asJava)) + Observable[R](asJavaObservable.flatMap[R](new Func1[T, rx.Observable[_ <: R]]{ + def call(t1: T): rx.Observable[_ <: R] = { f(t1).asJavaObservable } + })) } - + /** * Returns an Observable that applies the given function to each item emitted by an * Observable and emits the result. * * - * + * * @param func * a function to apply to each item emitted by the Observable * @return an Observable that emits the items from the source Observable, transformed by the * given function */ def map[R](func: T => R): Observable[R] = { - Observable[R](asJava.map[R](func)) + Observable[R](asJavaObservable.map[R](new Func1[T,R] { + def call(t1: T): R = func(t1) + })) } - + /** - * Turns all of the notifications from a source Observable into [[Observer.onNext onNext]] emissions, and marks them with their original notification types within [[Notification]] objects. + * Turns all of the notifications from a source Observable into [[rx.lang.scala.Observer.onNext onNext]] emissions, + * and marks them with their original notification types within [[rx.lang.scala.Notification]] objects. * * - * + * * @return an Observable whose items are the result of materializing the items and * notifications of the source Observable */ def materialize: Observable[Notification[T]] = { - Observable[rx.Notification[_ <: T]](asJava.materialize()).map(Notification(_)) + Observable[rx.Notification[_ <: T]](asJavaObservable.materialize()).map(Notification(_)) } /** - * Asynchronously subscribes and unsubscribes Observers on the specified [[Scheduler]]. + * Asynchronously subscribes and unsubscribes Observers on the specified [[rx.lang.scala.Scheduler]]. * * - * + * * @param scheduler - * the [[Scheduler]] to perform subscription and unsubscription actions on + * the [[rx.lang.scala.Scheduler]] to perform subscription and unsubscription actions on * @return the source Observable modified so that its subscriptions and unsubscriptions happen - * on the specified [[Scheduler]] + * on the specified [[rx.lang.scala.Scheduler]] */ def subscribeOn(scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.subscribeOn(scheduler)) - } + Observable[T](asJavaObservable.subscribeOn(scheduler)) + } /** - * Asynchronously notify [[Observer]]s on the specified [[Scheduler]]. + * Asynchronously notify [[rx.lang.scala.Observer]]s on the specified [[rx.lang.scala.Scheduler]]. * * - * + * * @param scheduler - * the [[Scheduler]] to notify [[Observer]]s on - * @return the source Observable modified so that its [[Observer]]s are notified on the - * specified [[Scheduler]] + * the [[rx.lang.scala.Scheduler]] to notify [[rx.lang.scala.Observer]]s on + * @return the source Observable modified so that its [[rx.lang.scala.Observer]]s are notified on the + * specified [[rx.lang.scala.Scheduler]] */ def observeOn(scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.observeOn(scheduler)) + Observable[T](asJavaObservable.observeOn(scheduler)) } - + /** - * Returns an Observable that reverses the effect of [[Observable.materialize]] by - * transforming the [[Notification]] objects emitted by the source Observable into the items + * Returns an Observable that reverses the effect of [[rx.lang.scala.Observable.materialize]] by + * transforming the [[rx.lang.scala.Notification]] objects emitted by the source Observable into the items * or notifications they represent. - * + * * This operation is only available if `this` is of type `Observable[Notification[U]]` for some `U`, * otherwise you will get a compilation error. * * - * - * @return an Observable that emits the items and notifications embedded in the [[Notification]] objects emitted by the source Observable - * + * + * @return an Observable that emits the items and notifications embedded in the [[rx.lang.scala.Notification]] objects emitted by the source Observable + * * @usecase def dematerialize[U]: Observable[U] * @inheritdoc - * + * */ // with =:= it does not work, why? def dematerialize[U](implicit evidence: Observable[T] <:< Observable[Notification[U]]): Observable[U] = { val o1: Observable[Notification[U]] = this val o2: Observable[rx.Notification[_ <: U]] = o1.map(_.asJava) - val o3 = o2.asJava.dematerialize[U]() + val o3 = o2.asJavaObservable.dematerialize[U]() Observable[U](o3) } - + /** - * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error. + * Instruct an Observable to pass control to another Observable rather than invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error. * * * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[Observer]], the Observable invokes its Observer's + * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's * `onError` method, and then quits without invoking any more of its Observer's * methods. The `onErrorResumeNext` method changes this behavior. If you pass a * function that returns an Observable (`resumeFunction`) to * `onErrorResumeNext`, if the original Observable encounters an error, instead of * invoking its Observer's `onError` method, it will instead relinquish control to * the Observable returned from `resumeFunction`, which will invoke the Observer's - * [[Observer.onNext onNext]] method if it is able to do so. In such a case, because no + * [[rx.lang.scala.Observer.onNext 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 * a function that returns an Observable that will take over if the source Observable * encounters an error * @return the original Observable, with appropriately modified behavior */ def onErrorResumeNext[U >: T](resumeFunction: Throwable => Observable[U]): Observable[U] = { - val f: Func1[Throwable, rx.Observable[_ <: U]] = (t: Throwable) => resumeFunction(t).asJava + val f: Func1[Throwable, rx.Observable[_ <: U]] = (t: Throwable) => resumeFunction(t).asJavaObservable val f2 = f.asInstanceOf[Func1[Throwable, rx.Observable[Nothing]]] - Observable[U](asJava.onErrorResumeNext(f2)) + Observable[U](asJavaObservable.onErrorResumeNext(f2)) } - + /** - * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error. + * Instruct an Observable to pass control to another Observable rather than invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error. * * * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[Observer]], the Observable invokes its Observer's + * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's * `onError` method, and then quits without invoking any more of its Observer's * methods. The `onErrorResumeNext` method changes this behavior. If you pass * another Observable (`resumeSequence`) to an Observable's * `onErrorResumeNext` method, if the original Observable encounters an error, * instead of invoking its Observer's `onError` method, it will instead relinquish - * control to `resumeSequence` which will invoke the Observer's [[Observer.onNext onNext]] + * control to `resumeSequence` which will invoke the Observer's [[rx.lang.scala.Observer.onNext 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 * a function that returns an Observable that will take over if the source Observable * encounters an error * @return the original Observable, with appropriately modified behavior */ def onErrorResumeNext[U >: T](resumeSequence: Observable[U]): Observable[U] = { - val rSeq1: rx.Observable[_ <: U] = resumeSequence.asJava + val rSeq1: rx.Observable[_ <: U] = resumeSequence.asJavaObservable val rSeq2: rx.Observable[Nothing] = rSeq1.asInstanceOf[rx.Observable[Nothing]] - Observable[U](asJava.onErrorResumeNext(rSeq2)) + Observable[U](asJavaObservable.onErrorResumeNext(rSeq2)) } /** - * Instruct an Observable to pass control to another Observable rather than invoking [[Observer.onError onError]] if it encounters an error of type `java.lang.Exception`. + * Instruct an Observable to pass control to another Observable rather than invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error of type `java.lang.Exception`. * * This differs from `Observable.onErrorResumeNext` in that this one does not handle `java.lang.Throwable` or `java.lang.Error` but lets those continue through. * * * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[Observer]], the Observable invokes its Observer's + * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's * `onError` method, and then quits without invoking any more of its Observer's * methods. The `onErrorResumeNext` method changes this behavior. If you pass * another Observable (`resumeSequence`) to an Observable's * `onErrorResumeNext` method, if the original Observable encounters an error, * instead of invoking its Observer's `onError` method, it will instead relinquish - * control to `resumeSequence` which will invoke the Observer's [[Observer.onNext onNext]] + * control to `resumeSequence` which will invoke the Observer's [[rx.lang.scala.Observer.onNext 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 * a function that returns an Observable that will take over if the source Observable * encounters an error * @return the original Observable, with appropriately modified behavior - */ + */ def onExceptionResumeNext[U >: T](resumeSequence: Observable[U]): Observable[U] = { - val rSeq1: rx.Observable[_ <: U] = resumeSequence.asJava + val rSeq1: rx.Observable[_ <: U] = resumeSequence.asJavaObservable val rSeq2: rx.Observable[Nothing] = rSeq1.asInstanceOf[rx.Observable[Nothing]] - Observable[U](asJava.onExceptionResumeNext(rSeq2)) + Observable[U](asJavaObservable.onExceptionResumeNext(rSeq2)) } /** * Instruct an Observable to emit an item (returned by a specified function) rather than - * invoking [[Observer.onError onError]] if it encounters an error. + * invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error. * * * * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[Observer]], the Observable invokes its Observer's + * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's * `onError` method, and then quits without invoking any more of its Observer's * methods. The `onErrorReturn` method changes this behavior. If you pass a function * (`resumeFunction`) to an Observable's `onErrorReturn` method, if the * original Observable encounters an error, instead of invoking its Observer's * `onError` method, it will instead pass the return value of - * `resumeFunction` to the Observer's [[Observer.onNext onNext]] method. + * `resumeFunction` to the Observer's [[rx.lang.scala.Observer.onNext onNext]] method. * * You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. - * + * * @param resumeFunction * a function that returns an item that the new Observable will emit if the source * Observable encounters an error @@ -940,8 +956,8 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) def onErrorReturn[U >: T](resumeFunction: Throwable => U): Observable[U] = { val f1: Func1[Throwable, _ <: U] = resumeFunction val f2 = f1.asInstanceOf[Func1[Throwable, Nothing]] - Observable[U](asJava.onErrorReturn(f2)) - } + Observable[U](asJavaObservable.onErrorReturn(f2)) + } /** * Returns an Observable that applies a function of your choosing to the first item emitted by a @@ -955,69 +971,69 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * This technique, which is called "reduce" or "aggregate" 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 function to be invoked on each item emitted by the source * Observable, whose result will be used in the next accumulator call * @return an Observable that emits a single item that is the result of accumulating the * output from the source Observable */ - def reduce[U >: T](f: (U, U) => U): Observable[U] = { - val func: Func2[_ >: U, _ >: U, _ <: U] = f + def reduce[U >: T](accumulator: (U, U) => U): Observable[U] = { + val func: Func2[_ >: U, _ >: U, _ <: U] = accumulator val func2 = func.asInstanceOf[Func2[T, T, T]] - Observable[U](asJava.asInstanceOf[rx.Observable[T]].reduce(func2)) - } + Observable[U](asJavaObservable.asInstanceOf[rx.Observable[T]].reduce(func2)) + } /** - * Returns a pair of a start function and an [[Observable]] that shares a single subscription to the underlying - * Observable that will replay all of its items and notifications to any future [[Observer]]. + * Returns a pair of a start function and an [[rx.lang.scala.Observable]] that shares a single subscription to the underlying + * Observable that will replay all of its items and notifications to any future [[rx.lang.scala.Observer]]. * * - * - * @return a pair of a start function and an [[Observable]] such that when the start function - * is called, the Observable starts to emit items to its [[Observer]]s + * + * @return a pair of a start function and an [[rx.lang.scala.Observable]] such that when the start function + * is called, the Observable starts to emit items to its [[rx.lang.scala.Observer]]s */ def replay: (() => Subscription, Observable[T]) = { - val javaCO = asJava.replay() + val javaCO = asJavaObservable.replay() (() => javaCO.connect(), Observable[T](javaCO)) } /** - * This method has similar behavior to [[Observable.replay]] except that this auto-subscribes to + * This method has similar behavior to [[rx.lang.scala.Observable.replay]] except that this auto-subscribes to * the source Observable rather than returning a start function and an Observable. * * * * This is useful when you want an Observable to cache responses and you can't control the - * subscribe/unsubscribe behavior of all the [[Observer]]s. + * subscribe/unsubscribe behavior of all the [[rx.lang.scala.Observer]]s. * * NOTE: You sacrifice the ability to unsubscribe from the origin when you use the * `cache()` operator so be careful not to use this operator on Observables that * emit an infinite or very large number of items that will use up memory. - * + * * @return an Observable that when first subscribed to, caches all of its notifications for * the benefit of subsequent subscribers. */ def cache: Observable[T] = { - Observable[T](asJava.cache()) + Observable[T](asJavaObservable.cache()) } /** - * Returns a a pair of a start function and an [[Observable]], which waits until the start function is called before it begins emitting - * items to those [[Observer]]s that have subscribed to it. + * Returns a a pair of a start function and an [[rx.lang.scala.Observable]], which waits until the start function is called before it begins emitting + * items to those [[rx.lang.scala.Observer]]s that have subscribed to it. * * - * - * @return a pair of a start function and an [[Observable]] such that when the start function - * is called, the Observable starts to emit items to its [[Observer]]s + * + * @return a pair of a start function and an [[rx.lang.scala.Observable]] such that when the start function + * is called, the Observable starts to emit items to its [[rx.lang.scala.Observer]]s */ def publish: (() => Subscription, Observable[T]) = { - val javaCO = asJava.publish() + val javaCO = asJavaObservable.publish() (() => javaCO.connect(), Observable[T](javaCO)) } // TODO add Scala-like aggregate function - + /** * Returns an Observable that applies a function of your choosing to the first item emitted by a * source Observable, then feeds the result of that function along with the second item emitted @@ -1030,7 +1046,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * This technique, which is called "reduce" or "aggregate" 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 @@ -1040,24 +1056,23 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * from the items emitted by the source Observable */ def foldLeft[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { - Observable[R](asJava.reduce(initialValue, accumulator)) + Observable[R](asJavaObservable.reduce(initialValue, new Func2[R,T,R]{ + def call(t1: R, t2: T): R = accumulator(t1,t2) + })) } - + /** * Returns an Observable that emits the results of sampling the items emitted by the source * Observable at a specified time interval. * * - * - * @param period - * the sampling rate - * @param unit - * the [[TimeUnit]] in which `period` is defined + * + * @param duration the sampling rate * @return an Observable that emits the results of sampling the items emitted by the source * Observable at the specified time interval */ def sample(duration: Duration): Observable[T] = { - Observable[T](asJava.sample(duration.length, duration.unit)) + Observable[T](asJavaObservable.sample(duration.length, duration.unit)) } /** @@ -1065,20 +1080,17 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Observable at a specified time interval. * * - * - * @param period - * the sampling rate - * @param unit - * the [[TimeUnit]] in which `period` is defined + * + * @param duration the sampling rate * @param scheduler - * the [[Scheduler]] to use when sampling + * the [[rx.lang.scala.Scheduler]] to use when sampling * @return an Observable that emits the results of sampling the items emitted by the source * Observable at the specified time interval */ def sample(duration: Duration, scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.sample(duration.length, duration.unit, scheduler)) + Observable[T](asJavaObservable.sample(duration.length, duration.unit, scheduler)) } - + /** * Returns an Observable that applies a function of your choosing to the first item emitted by a * source Observable, then feeds the result of that function along with the second item emitted @@ -1091,16 +1103,18 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * Note that when you pass a seed to `scan()` the resulting Observable will emit * that seed as its first emitted item. - * + * * @param initialValue * the initial (seed) accumulator value * @param accumulator * an accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be emitted to [[Observer]]s via [[Observer.onNext onNext]] and used in the next accumulator call. + * Observable, whose result will be emitted to [[rx.lang.scala.Observer]]s via [[rx.lang.scala.Observer.onNext onNext]] and used in the next accumulator call. * @return an Observable that emits the results of each call to the accumulator function */ def scan[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { - Observable[R](asJava.scan(initialValue, accumulator)) + Observable[R](asJavaObservable.scan(initialValue, new Func2[R,T,R]{ + def call(t1: R, t2: T): R = accumulator(t1,t2) + })) } /** @@ -1108,7 +1122,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * the source Observable satisfy a condition. * * - * + * * @param predicate * a function that evaluates an item and returns a Boolean * @return an Observable that emits `true` if all items emitted by the source @@ -1120,20 +1134,20 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) // it's more fun in Scala: this.map(predicate).foldLeft(true)(_ && _) } - + /** * Returns an Observable that skips the first `num` items emitted by the source * Observable and emits the remainder. * * * - * @param num + * @param n * the number of items to skip * @return an Observable that is identical to the source Observable except that it does not * emit the first `num` items that the source emits */ def drop(n: Int): Observable[T] = { - Observable[T](asJava.skip(n)) + Observable[T](asJavaObservable.skip(n)) } /** @@ -1148,7 +1162,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * becomes false. */ def dropWhile(predicate: T => Boolean): Observable[T] = { - Observable[T](asJava.skipWhile(predicate)) + Observable(asJavaObservable.skipWhile(predicate)) } /** @@ -1157,18 +1171,18 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * This method returns an Observable that will invoke a subscribing [[Observer]]'s - * [[Observer.onNext onNext]] function a maximum of `num` times before invoking - * [[Observer.onCompleted onCompleted]]. - * - * @param num + * This method returns an Observable that will invoke a subscribing [[rx.lang.scala.Observer]]'s + * [[rx.lang.scala.Observer.onNext onNext]] function a maximum of `num` times before invoking + * [[rx.lang.scala.Observer.onCompleted onCompleted]]. + * + * @param n * the number of items to take * @return an 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 */ def take(n: Int): Observable[T] = { - Observable[T](asJava.take(n)) + Observable[T](asJavaObservable.take(n)) } /** @@ -1176,7 +1190,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * specified condition is true. * * - * + * * @param predicate * a function that evaluates an item emitted by the source Observable and returns a * Boolean @@ -1184,7 +1198,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * satisfies the condition defined by `predicate` */ def takeWhile(predicate: T => Boolean): Observable[T] = { - Observable[T](asJava.takeWhile(predicate)) + Observable[T](asJavaObservable.takeWhile(predicate)) } /** @@ -1192,23 +1206,23 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * Observable. * * - * + * * @param count * the number of items to emit from the end of the sequence emitted by the source * Observable * @return an Observable that emits only the last `count` items emitted by the source * Observable */ - def takeRight(n: Int): Observable[T] = { - Observable[T](asJava.takeLast(n)) + def takeRight(count: Int): Observable[T] = { + Observable[T](asJavaObservable.takeLast(count)) } - + /** * Returns an Observable that emits the items from the source Observable only until the * `other` Observable emits an item. * * - * + * * @param that * the Observable whose first emitted item will cause `takeUntil` to stop * emitting items from the source Observable @@ -1218,35 +1232,35 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * `other` emits its first item */ def takeUntil[E](that: Observable[E]): Observable[T] = { - Observable[T](asJava.takeUntil(that.asJava)) - } - + Observable[T](asJavaObservable.takeUntil(that.asJavaObservable)) + } + /** * Returns an Observable that emits a single item, a list composed of all the items emitted by * the source Observable. * * * - * Normally, an Observable that returns multiple items will do so by invoking its [[Observer]]'s - * [[Observer.onNext onNext]] method for each such item. You can change + * Normally, an Observable that returns multiple items will do so by invoking its [[rx.lang.scala.Observer]]'s + * [[rx.lang.scala.Observer.onNext onNext]] method for each such item. You can change * this behavior, instructing the Observable to compose a list of all of these items and then to * invoke the Observer's `onNext` function once, passing it the entire list, by * calling the Observable's `toList` method prior to calling its `Observable.subscribe` method. * * Be careful not to use this operator on Observables that emit infinite or very large numbers * of items, as you do not have the option to unsubscribe. - * + * * @return an Observable that emits a single item: a List containing all of the items emitted by * the source Observable. */ def toSeq: Observable[Seq[T]] = { - Observable.jObsOfListToScObsOfSeq(asJava.toList()) - : Observable[Seq[T]] // SI-7818 + Observable.jObsOfListToScObsOfSeq(asJavaObservable.toList) + : Observable[Seq[T]] // SI-7818 } - + /** * Groups the items emitted by this Observable according to a specified discriminator function. - * + * * @param f * a function that extracts the key from an item * @tparam K @@ -1255,59 +1269,57 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * contains all items for which `f` returned `key`. */ def groupBy[K](f: T => K): Observable[(K, Observable[T])] = { - val o1 = asJava.groupBy[K](f) : rx.Observable[_ <: rx.observables.GroupedObservable[K, _ <: T]] - val func = (o: rx.observables.GroupedObservable[K, _ <: T]) => (o.getKey(), Observable[T](o)) + val o1 = asJavaObservable.groupBy[K](f) : rx.Observable[_ <: rx.observables.GroupedObservable[K, _ <: T]] + val func = (o: rx.observables.GroupedObservable[K, _ <: T]) => (o.getKey, Observable[T](o)) Observable[(K, Observable[T])](o1.map[(K, Observable[T])](func)) } - + /** * Given an Observable that emits Observables, creates a single Observable that * emits the items emitted by the most recently published of those Observables. * * - * + * * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, * otherwise you'll get a compilation error. - * - * @param sequenceOfSequences - * the source Observable that emits Observables + * * @return an Observable that emits only the items emitted by the most recently published * Observable - * + * * @usecase def switch[U]: Observable[U] - * @inheritdoc + * @inheritdoc */ def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable val o5 = rx.Observable.switchOnNext[U](o4) Observable[U](o5) } // Naming: We follow C# (switch), not Java (switchOnNext), because Java just had to avoid clash with keyword - - /** + + /** * Flattens two Observables into one Observable, without any transformation. * * * * You can combine items emitted by two Observables so that they act like a single * Observable by using the `merge` method. - * + * * @param that * an Observable to be merged * @return an Observable that emits items from `this` and `that` until * `this` or `that` emits `onError` or `onComplete`. */ def merge[U >: T](that: Observable[U]): Observable[U] = { - val thisJava: rx.Observable[_ <: U] = this.asJava - val thatJava: rx.Observable[_ <: U] = that.asJava + val thisJava: rx.Observable[_ <: U] = this.asJavaObservable + val thatJava: rx.Observable[_ <: U] = that.asJavaObservable Observable[U](rx.Observable.merge(thisJava, thatJava)) } /** - * This behaves like [[Observable.merge]] except that if any of the merged Observables - * notify of an error via [[Observer.onError onError]], `mergeDelayError` will + * This behaves like [[rx.lang.scala.Observable.merge]] except that if any of the merged Observables + * notify of an error via [[rx.lang.scala.Observer.onError onError]], `mergeDelayError` will * refrain from propagating that error notification until all of the merged Observables have * finished emitting items. * @@ -1325,7 +1337,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * `this` and `that` */ def mergeDelayError[U >: T](that: Observable[U]): Observable[U] = { - Observable[U](rx.Observable.mergeDelayError[U](this.asJava, that.asJava)) + Observable[U](rx.Observable.mergeDelayError[U](this.asJavaObservable, that.asJavaObservable)) } /** @@ -1339,24 +1351,24 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, * otherwise you'll get a compilation error. - * + * * @return an Observable that emits items that are the result of flattening the items emitted * by the Observables emitted by `this` - * + * * @usecase def flatten[U]: Observable[U] - * @inheritdoc + * @inheritdoc */ def flatten[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable val o5 = rx.Observable.merge[U](o4) Observable[U](o5) } /** * This behaves like `flatten` except that if any of the merged Observables - * notify of an error via [[Observer.onError onError]], this method will + * notify of an error via [[rx.lang.scala.Observer.onError onError]], this method will * refrain from propagating that error notification until all of the merged Observables have * finished emitting items. * @@ -1367,20 +1379,20 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. - * + * * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, * otherwise you'll get a compilation error. * * @return an Observable that emits items that are the result of flattening the items emitted by * the Observables emitted by the this Observable - * + * * @usecase def flattenDelayError[U]: Observable[U] * @inheritdoc */ def flattenDelayError[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJava) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJava + val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) + val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable val o5 = rx.Observable.mergeDelayError[U](o4) Observable[U](o5) } @@ -1396,7 +1408,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) */ def combineLatest[U](that: Observable[U]): Observable[(T, U)] = { val f: Func2[_ >: T, _ >: U, _ <: (T, U)] = (t: T, u: U) => (t, u) - Observable[(T, U)](rx.Observable.combineLatest[T, U, (T, U)](this.asJava, that.asJava, f)) + Observable[(T, U)](rx.Observable.combineLatest[T, U, (T, U)](this.asJavaObservable, that.asJavaObservable, f)) } /** @@ -1409,13 +1421,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * $debounceVsThrottle * * @param timeout - * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. * - * @return An [[Observable]] which filters out values which are too quickly followed up with newer values. + * @return An [[rx.lang.scala.Observable]] which filters out values which are too quickly followed up with newer values. * @see `Observable.debounce` */ def throttleWithTimeout(timeout: Duration): Observable[T] = { - Observable[T](asJava.throttleWithTimeout(timeout.length, timeout.unit)) + Observable[T](asJavaObservable.throttleWithTimeout(timeout.length, timeout.unit)) } /** @@ -1428,13 +1440,13 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * $debounceVsThrottle * * @param timeout - * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. * - * @return An [[Observable]] which filters out values which are too quickly followed up with newer values. + * @return An [[rx.lang.scala.Observable]] which filters out values which are too quickly followed up with newer values. * @see `Observable.throttleWithTimeout` */ def debounce(timeout: Duration): Observable[T] = { - Observable[T](asJava.debounce(timeout.length, timeout.unit)) + Observable[T](asJavaObservable.debounce(timeout.length, timeout.unit)) } /** @@ -1447,14 +1459,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * $debounceVsThrottle * * @param timeout - * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. * @param scheduler - * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. + * The [[rx.lang.scala.Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. * @see `Observable.throttleWithTimeout` */ def debounce(timeout: Duration, scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.debounce(timeout.length, timeout.unit, scheduler)) + Observable[T](asJavaObservable.debounce(timeout.length, timeout.unit, scheduler)) } /** @@ -1465,14 +1477,14 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * @param timeout - * The time each value has to be 'the most recent' of the [[Observable]] to ensure that it's not dropped. + * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. * @param scheduler - * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. + * The [[rx.lang.scala.Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. * @see `Observable.debounce` */ def throttleWithTimeout(timeout: Duration, scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.throttleWithTimeout(timeout.length, timeout.unit, scheduler)) + Observable[T](asJavaObservable.throttleWithTimeout(timeout.length, timeout.unit, scheduler)) } /** @@ -1485,11 +1497,11 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @param skipDuration * Time to wait before sending another value after emitting last value. * @param scheduler - * The [[Scheduler]] to use internally to manage the timers which handle timeout for each event. + * The [[rx.lang.scala.Scheduler]] to use internally to manage the timers which handle timeout for each event. * @return Observable which performs the throttle operation. */ def throttleFirst(skipDuration: Duration, scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.throttleFirst(skipDuration.length, skipDuration.unit, scheduler)) + Observable[T](asJavaObservable.throttleFirst(skipDuration.length, skipDuration.unit, scheduler)) } /** @@ -1504,7 +1516,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return Observable which performs the throttle operation. */ def throttleFirst(skipDuration: Duration): Observable[T] = { - Observable[T](asJava.throttleFirst(skipDuration.length, skipDuration.unit)) + Observable[T](asJavaObservable.throttleFirst(skipDuration.length, skipDuration.unit)) } /** @@ -1519,7 +1531,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return Observable which performs the throttle operation. */ def throttleLast(intervalDuration: Duration): Observable[T] = { - Observable[T](asJava.throttleLast(intervalDuration.length, intervalDuration.unit)) + Observable[T](asJavaObservable.throttleLast(intervalDuration.length, intervalDuration.unit)) } /** @@ -1534,34 +1546,34 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return Observable which performs the throttle operation. */ def throttleLast(intervalDuration: Duration, scheduler: Scheduler): Observable[T] = { - Observable[T](asJava.throttleLast(intervalDuration.length, intervalDuration.unit, scheduler)) + Observable[T](asJavaObservable.throttleLast(intervalDuration.length, intervalDuration.unit, scheduler)) } /** * Returns an Observable that sums up the elements of this Observable. - * + * * This operation is only available if the elements of this Observable are numbers, otherwise * you will get a compilation error. - * + * * @return an Observable emitting the sum of all the elements of the source Observable * as its single item. - * + * * @usecase def sum: Observable[T] * @inheritdoc */ def sum[U >: T](implicit num: Numeric[U]): Observable[U] = { foldLeft(num.zero)(num.plus) } - + /** * Returns an Observable that multiplies up the elements of this Observable. - * + * * This operation is only available if the elements of this Observable are numbers, otherwise * you will get a compilation error. - * + * * @return an Observable emitting the product of all the elements of the source Observable * as its single item. - * + * * @usecase def product: Observable[T] * @inheritdoc */ @@ -1612,7 +1624,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * source Observable completes without emitting a single item. */ def first: Observable[T] = take(1) - + /* TODO once https://github.com/Netflix/RxJava/issues/417 is fixed, we can add head and tail methods @@ -1633,7 +1645,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) def tail: Observable[T] = ??? */ - + /** * Returns an Observable that forwards all sequentially distinct items emitted from the source Observable. * @@ -1642,7 +1654,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable of sequentially distinct items */ def distinctUntilChanged: Observable[T] = { - Observable[T](asJava.distinctUntilChanged) + Observable[T](asJavaObservable.distinctUntilChanged) } /** @@ -1657,7 +1669,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable of sequentially distinct items */ def distinctUntilChanged[U](keySelector: T => U): Observable[T] = { - Observable[T](asJava.distinctUntilChanged[U](keySelector)) + Observable[T](asJavaObservable.distinctUntilChanged[U](keySelector)) } /** @@ -1668,7 +1680,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable of distinct items */ def distinct: Observable[T] = { - Observable[T](asJava.distinct()) + Observable[T](asJavaObservable.distinct()) } /** @@ -1683,7 +1695,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return an Observable of distinct items */ def distinct[U](keySelector: T => U): Observable[T] = { - Observable[T](asJava.distinct[U](keySelector)) + Observable[T](asJavaObservable.distinct[U](keySelector)) } /** @@ -1695,9 +1707,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * as its single item. */ def length: Observable[Int] = { - Observable[Integer](asJava.count()).map(_.intValue()) + Observable[Integer](asJavaObservable.count()).map(_.intValue()) } - + /** * Returns an Observable that counts the total number of elements in the source Observable. * @@ -1713,9 +1725,9 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * If [[Observer.onError]] is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. + * If [[rx.lang.scala.Observer.onError]] is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. * - * Any [[Observer.onNext]] calls received on each attempt will be emitted and concatenated together. + * Any [[rx.lang.scala.Observer.onNext]] calls received on each attempt will be emitted and concatenated together. * * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. @@ -1725,7 +1737,7 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @return Observable with retry logic. */ def retry(retryCount: Int): Observable[T] = { - Observable[T](asJava.retry(retryCount)) + Observable[T](asJavaObservable.retry(retryCount)) } /** @@ -1733,18 +1745,18 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * * * - * If [[Observer.onError]] is invoked the source Observable will be re-subscribed to. + * If [[rx.lang.scala.Observer.onError]] is invoked the source Observable will be re-subscribed to. * - * Any [[Observer.onNext]] calls received on each attempt will be emitted and concatenated together. + * Any [[rx.lang.scala.Observer.onNext]] calls received on each attempt will be emitted and concatenated together. * * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. * @return Observable with retry logic. */ def retry: Observable[T] = { - Observable[T](asJava.retry()) + Observable[T](asJavaObservable.retry()) } - + /** * Converts an Observable into a [[rx.lang.scala.observables.BlockingObservable]] (an Observable with blocking * operators). @@ -1752,80 +1764,62 @@ class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) * @see Blocking Observable Operators */ def toBlockingObservable: BlockingObservable[T] = { - new BlockingObservable[T](asJava.toBlockingObservable()) + new BlockingObservable[T](asJavaObservable.toBlockingObservable) } /** * Perform work in parallel by sharding an `Observable[T]` on a - * [[rx.lang.scala.concurrency.Schedulers.threadPoolForComputation computation]] - * [[Scheduler]] and return an `Observable[R]` with the output. + * [[rx.lang.scala.concurrency.Schedulers.threadPoolForComputation computation]] + * [[rx.lang.scala.Scheduler]] and return an `Observable[R]` with the output. * * @param f * a function that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` - * @return an Observable with the output of the function executed on a [[Scheduler]] + * @return an Observable with the output of the function executed on a [[rx.lang.scala.Scheduler]] */ def parallel[R](f: Observable[T] => Observable[R]): Observable[R] = { val fJava: Func1[rx.Observable[T], rx.Observable[R]] = - (jo: rx.Observable[T]) => f(Observable[T](jo)).asJava.asInstanceOf[rx.Observable[R]] - Observable[R](asJava.asInstanceOf[rx.Observable[T]].parallel[R](fJava)) + (jo: rx.Observable[T]) => f(Observable[T](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] + Observable[R](asJavaObservable.asInstanceOf[rx.Observable[T]].parallel[R](fJava)) } /** - * Perform work in parallel by sharding an `Observable[T]` on a [[Scheduler]] and return an `Observable[R]` with the output. + * Perform work in parallel by sharding an `Observable[T]` on a [[rx.lang.scala.Scheduler]] and return an `Observable[R]` with the output. * * @param f * a function that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` - * @param s - * a [[Scheduler]] to perform the work on. - * @return an Observable with the output of the function executed on a [[Scheduler]] + * @param scheduler + * a [[rx.lang.scala.Scheduler]] to perform the work on. + * @return an Observable with the output of the function executed on a [[rx.lang.scala.Scheduler]] */ def parallel[R](f: Observable[T] => Observable[R], scheduler: Scheduler): Observable[R] = { val fJava: Func1[rx.Observable[T], rx.Observable[R]] = - (jo: rx.Observable[T]) => f(Observable[T](jo)).asJava.asInstanceOf[rx.Observable[R]] - Observable[R](asJava.asInstanceOf[rx.Observable[T]].parallel[R](fJava, scheduler)) + (jo: rx.Observable[T]) => f(Observable[T](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] + Observable[R](asJavaObservable.asInstanceOf[rx.Observable[T]].parallel[R](fJava, scheduler)) } /** Tests whether a predicate holds for some of the elements of this `Observable`. - * - * @param p the predicate used to test elements. - * @return an Observable emitting one single Boolean, which is `true` if the given predicate `p` - * holds for some of the elements of this Observable, and `false` otherwise. - */ + * + * @param p the predicate used to test elements. + * @return an Observable emitting one single Boolean, which is `true` if the given predicate `p` + * holds for some of the elements of this Observable, and `false` otherwise. + */ def exists(p: T => Boolean): Observable[Boolean] = { - Observable[java.lang.Boolean](asJava.exists(p)).map(_.booleanValue()) + Observable[java.lang.Boolean](asJavaObservable.exists(p)).map(_.booleanValue()) } /** Tests whether this `Observable` emits no elements. - * - * @return an Observable emitting one single Boolean, which is `true` if this `Observable` - * emits no elements, and `false` otherwise. - */ + * + * @return an Observable emitting one single Boolean, which is `true` if this `Observable` + * emits no elements, and `false` otherwise. + */ def isEmpty: Observable[Boolean] = { - Observable[java.lang.Boolean](asJava.isEmpty).map(_.booleanValue()) - } - - def withFilter(p: T => Boolean): WithFilter[T] = { - new WithFilter[T](p, asJava) - } - - - def doOnEach(observer: Observer[T]): Observable[T] = { - Observable[T](asJava.doOnEach(observer)) - } - - def doOnEach(onNext: T => Unit): Observable[T] = { - Observable[T](asJava.doOnEach(onNext)) - } - - def doOnEach(onNext: T => Unit, onError: Throwable => Unit): Observable[T] = { - Observable[T](asJava.doOnEach(onNext, onError)) + Observable[java.lang.Boolean](asJavaObservable.isEmpty).map(_.booleanValue()) } - def doOnEach(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit): Observable[T] = { - Observable[T](asJava.doOnEach(onNext, onError, onComplete)) - } + //def withFilter(p: T => Boolean): WithFilter[T] = { + // new WithFilter[T](p, asJava) + //} - } /** @@ -1835,37 +1829,36 @@ object Observable { import scala.collection.JavaConverters._ import scala.collection.immutable.Range import scala.concurrent.duration.Duration - import rx.{Observable => JObservable} - import rx.lang.scala.util._ - import rx.util.functions._ - import rx.lang.scala.ImplicitFunctionConversions._ - - private[scala] + import ImplicitFunctionConversions._ + + private[scala] def jObsOfListToScObsOfSeq[T](jObs: rx.Observable[_ <: java.util.List[T]]): Observable[Seq[T]] = { - val oScala1: Observable[java.util.List[T]] = new Observable[java.util.List[T]](jObs) + val oScala1: Observable[java.util.List[T]] = new Observable[java.util.List[T]]{ def asJavaObservable = jObs } oScala1.map((lJava: java.util.List[T]) => lJava.asScala) } - - private[scala] + + private[scala] def jObsOfJObsToScObsOfScObs[T](jObs: rx.Observable[_ <: rx.Observable[_ <: T]]): Observable[Observable[T]] = { - val oScala1: Observable[rx.Observable[_ <: T]] = new Observable[rx.Observable[_ <: T]](jObs) - oScala1.map((oJava: rx.Observable[_ <: T]) => new Observable[T](oJava)) + val oScala1: Observable[rx.Observable[_ <: T]] = new Observable[rx.Observable[_ <: T]]{ def asJavaObservable = jObs } + oScala1.map((oJava: rx.Observable[_ <: T]) => new Observable[T]{ def asJavaObservable = oJava}) } - + /** * Creates a new Scala Observable from a given Java Observable. */ - def apply[T](asJava: rx.Observable[_ <: T]): Observable[T] = { - new Observable[T](asJava) + def apply[T](observable: rx.Observable[_ <: T]): Observable[T] = { + new Observable[T]{ + def asJavaObservable = observable + } } - + /** - * Creates an Observable that will execute the given function when an [[Observer]] subscribes to it. + * Creates an Observable that will execute the given function when an [[rx.lang.scala.Observer]] subscribes to it. * * * * Write the function you pass to `create` so that it behaves as an Observable: It - * should invoke the Observer's [[Observer.onNext onNext]], [[Observer.onError onError]], and [[Observer.onCompleted onCompleted]] methods + * should invoke the Observer's [[rx.lang.scala.Observer.onNext onNext]], [[rx.lang.scala.Observer.onError onError]], and [[rx.lang.scala.Observer.onCompleted onCompleted]] methods * appropriately. * * A well-formed Observable must invoke either the Observer's `onCompleted` method @@ -1873,45 +1866,49 @@ object Observable { * * See Rx Design Guidelines (PDF) * for detailed information. - * - * + * + * * @tparam T * the type of the items that this Observable emits * @param func * a function that accepts an `Observer[T]`, invokes its `onNext`, `onError`, and `onCompleted` methods - * as appropriate, and returns a [[Subscription]] to allow the Observer to + * as appropriate, and returns a [[rx.lang.scala.Subscription]] to allow the Observer to * canceling the subscription - * @return an Observable that, when an [[Observer]] subscribes to it, will execute the given + * @return an Observable that, when an [[rx.lang.scala.Observer]] subscribes to it, will execute the given * function */ def apply[T](func: Observer[T] => Subscription): Observable[T] = { - Observable[T](JObservable.create(func)) + Observable[T](rx.Observable.create(new OnSubscribeFunc[T] { + def onSubscribe(t1: rx.Observer[_ >: T]): rx.Subscription = { + func(Observer(t1)) + } + })) } - + /** - * Returns an Observable that invokes an [[Observer]]'s [[Observer.onError onError]] method when the Observer subscribes to it + * Returns an Observable that invokes an [[rx.lang.scala.Observer]]'s [[rx.lang.scala.Observer.onError onError]] method when the Observer subscribes to it * * - * + * * @param exception * the particular error to report * @tparam T * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the [[Observer]]'s [[Observer.onError onError]] method when the Observer subscribes to it + * @return an Observable that invokes the [[rx.lang.scala.Observer]]'s [[rx.lang.scala.Observer.onError onError]] method when the Observer subscribes to it */ def apply[T](exception: Throwable): Observable[T] = { - Observable[T](JObservable.error(exception)) + Observable[T](rx.Observable.error(exception)) } /** * Converts a sequence of values into an Observable. * * - * - * Implementation note: the entire array will be immediately emitted each time an [[Observer]] subscribes. - * Since this occurs before the [[Subscription]] is returned, + * + * Implementation note: the entire array will be immediately emitted each time an [[rx.lang.scala.Observer]] subscribes. + * Since this occurs before the [[rx.lang.scala.Subscription]] is returned, * it in not possible to unsubscribe from the sequence before it completes. - * + * * @param items * the source Array * @tparam T @@ -1919,93 +1916,93 @@ object Observable { * resulting Observable * @return an Observable that emits each item in the source Array */ - def apply[T](args: T*): Observable[T] = { - Observable[T](JObservable.from(args.toIterable.asJava)) + def apply[T](items: T*): Observable[T] = { + Observable[T](rx.Observable.from(items.toIterable.asJava)) } /** * Generates an Observable that emits a sequence of integers within a specified range. - * + * * * - * Implementation note: the entire range will be immediately emitted each time an [[Observer]] subscribes. - * Since this occurs before the [[Subscription]] is returned, + * Implementation note: the entire range will be immediately emitted each time an [[rx.lang.scala.Observer]] subscribes. + * Since this occurs before the [[rx.lang.scala.Subscription]] is returned, * it in not possible to unsubscribe from the sequence before it completes. * * @param range the range * @return an Observable that emits a range of sequential integers */ def apply(range: Range): Observable[Int] = { - Observable[Int](JObservable.from(range.toIterable.asJava)) + Observable[Int](rx.Observable.from(range.toIterable.asJava)) } - + /** * Returns an Observable that calls an Observable factory to create its Observable for each * new Observer that subscribes. That is, for each subscriber, the actual Observable is determined * by the factory function. - * + * * * * The defer operator allows you to defer or delay emitting items from an Observable until such - * time as an Observer subscribes to the Observable. This allows an [[Observer]] to easily + * time as an Observer subscribes to the Observable. This allows an [[rx.lang.scala.Observer]] to easily * obtain updates or a refreshed version of the sequence. - * - * @param observableFactory - * the Observable factory function to invoke for each [[Observer]] that + * + * @param observable + * the Observable factory function to invoke for each [[rx.lang.scala.Observer]] that * subscribes to the resulting Observable * @tparam T * the type of the items emitted by the Observable - * @return an Observable whose [[Observer]]s trigger an invocation of the given Observable + * @return an Observable whose [[rx.lang.scala.Observer]]s trigger an invocation of the given Observable * factory function */ def defer[T](observable: => Observable[T]): Observable[T] = { - Observable[T](JObservable.defer[T](() => observable.asJava)) + Observable[T](rx.Observable.defer[T](() => observable.asJavaObservable)) } - + /** - * Returns an Observable that never sends any items or notifications to an [[Observer]]. + * Returns an Observable that never sends any items or notifications to an [[rx.lang.scala.Observer]]. * * * * This Observable is useful primarily for testing purposes. - * - * @return an Observable that never sends any items or notifications to an [[Observer]] + * + * @return an Observable that never sends any items or notifications to an [[rx.lang.scala.Observer]] */ def never: Observable[Nothing] = { - Observable[Nothing](JObservable.never()) + Observable[Nothing](rx.Observable.never()) } /** * Given 3 observables, returns an observable that emits Tuples of 3 elements each. * The first emitted Tuple will contain the first element of each source observable, * the second Tuple the second element of each source observable, and so on. - * + * * @return an Observable that emits the zipped Observables */ def zip[A, B, C](obA: Observable[A], obB: Observable[B], obC: Observable[C]): Observable[(A, B, C)] = { - Observable[(A, B, C)](rx.Observable.zip[A, B, C, (A, B, C)](obA.asJava, obB.asJava, obC.asJava, (a: A, b: B, c: C) => (a, b, c))) + Observable[(A, B, C)](rx.Observable.zip[A, B, C, (A, B, C)](obA.asJavaObservable, obB.asJavaObservable, obC.asJavaObservable, (a: A, b: B, c: C) => (a, b, c))) } - + /** * Given 4 observables, returns an observable that emits Tuples of 4 elements each. * The first emitted Tuple will contain the first element of each source observable, * the second Tuple the second element of each source observable, and so on. - * + * * @return an Observable that emits the zipped Observables */ def zip[A, B, C, D](obA: Observable[A], obB: Observable[B], obC: Observable[C], obD: Observable[D]): Observable[(A, B, C, D)] = { - Observable[(A, B, C, D)](rx.Observable.zip[A, B, C, D, (A, B, C, D)](obA.asJava, obB.asJava, obC.asJava, obD.asJava, (a: A, b: B, c: C, d: D) => (a, b, c, d))) + Observable[(A, B, C, D)](rx.Observable.zip[A, B, C, D, (A, B, C, D)](obA.asJavaObservable, obB.asJavaObservable, obC.asJavaObservable, obD.asJavaObservable, (a: A, b: B, c: C, d: D) => (a, b, c, d))) } /** - * Given an Observable emitting `N` source observables, returns an observable that + * Given an Observable emitting `N` source observables, returns an observable that * emits Seqs of `N` elements each. * The first emitted Seq will contain the first element of each source observable, * the second Seq the second element of each source observable, and so on. - * - * Note that the returned Observable will only start emitting items once the given + * + * Note that the returned Observable will only start emitting items once the given * `Observable[Observable[T]]` has completed, because otherwise it cannot know `N`. - * + * * @param observables * An Observable emitting N source Observables * @return an Observable that emits the zipped Seqs @@ -2015,14 +2012,14 @@ object Observable { val asSeq: Seq[Object] = args.toSeq asSeq.asInstanceOf[Seq[T]] } - val list = observables.map(_.asJava).asJava + val list = observables.map(_.asJavaObservable).asJavaObservable val o = rx.Observable.zip(list, f) Observable[Seq[T]](o) } /** * Emits `0`, `1`, `2`, `...` with a delay of `duration` between consecutive numbers. - * + * * * * @param duration @@ -2030,12 +2027,13 @@ object Observable { * @return An Observable that emits a number each time interval. */ def interval(duration: Duration): Observable[Long] = { - (new Observable[java.lang.Long](JObservable.interval(duration.length, duration.unit))).map(_.longValue()) + Observable[java.lang.Long](rx.Observable.interval(duration.length, duration.unit)).map(_.longValue()) + /*XXX*/ } /** * Emits `0`, `1`, `2`, `...` with a delay of `duration` between consecutive numbers. - * + * * * * @param duration @@ -2045,116 +2043,14 @@ object Observable { * @return An Observable that emits a number each time interval. */ def interval(duration: Duration, scheduler: Scheduler): Observable[Long] = { - (new Observable[java.lang.Long](JObservable.interval(duration.length, duration.unit, scheduler))).map(_.longValue()) + Observable[java.lang.Long](rx.Observable.interval(duration.length, duration.unit, scheduler)).map(_.longValue()) + /*XXX*/ } - -} -// Cannot yet have inner class because of this error message: -// "implementation restriction: nested class is not allowed in value class. -// This restriction is planned to be removed in subsequent releases." -private[scala] class WithFilter[+T] (p: T => Boolean, asJava: rx.Observable[_ <: T]) { - import rx.lang.scala.ImplicitFunctionConversions._ - - def map[B](f: T => B): Observable[B] = { - Observable[B](asJava.filter(p).map[B](f)) - } - - def flatMap[B](f: T => Observable[B]): Observable[B] = { - Observable[B](asJava.filter(p).flatMap[B]((x: T) => f(x).asJava)) - } - - def withFilter(q: T => Boolean): Observable[T] = { - Observable[T](asJava.filter((x: T) => p(x) && q(x))) - } - - // there is no foreach here, that's only available on BlockingObservable } -private[scala] class UnitTestSuite extends org.scalatest.junit.JUnitSuite { - import scala.concurrent.duration._ - import org.junit.{Before, Test, Ignore} - import org.junit.Assert._ - import org.mockito.Matchers._ - import org.mockito.Mockito._ - import org.mockito.{ MockitoAnnotations, Mock } - - // Tests which needn't be run: - - @Ignore def testCovariance = { - println("hey, you shouldn't run this test") - - val o1: Observable[Nothing] = Observable() - val o2: Observable[Int] = o1 - val o3: Observable[App] = o1 - val o4: Observable[Any] = o2 - val o5: Observable[Any] = o3 - } - - // Tests which have to be run: - - @Test def testDematerialize() { - val o = Observable(1, 2, 3) - val mat = o.materialize - val demat = mat.dematerialize - - // correctly rejected: - // val wrongDemat = Observable("hello").dematerialize - - assertEquals(demat.toBlockingObservable.toIterable.toList, List(1, 2, 3)) - } - - // Test that Java's firstOrDefault propagates errors. - // If this changes (i.e. it suppresses errors and returns default) then Scala's firstOrElse - // should be changed accordingly. - @Test def testJavaFirstOrDefault() { - assertEquals(1, rx.Observable.from(1, 2).firstOrDefault(10).toBlockingObservable().single) - assertEquals(10, rx.Observable.empty().firstOrDefault(10).toBlockingObservable().single) - val msg = "msg6251" - var receivedMsg = "none" - try { - rx.Observable.error(new Exception(msg)).firstOrDefault(10).toBlockingObservable().single - } catch { - case e: Exception => receivedMsg = e.getCause().getMessage() - } - assertEquals(receivedMsg, msg) - } - - @Test def testFirstOrElse() { - def mustNotBeCalled: String = sys.error("this method should not be called") - def mustBeCalled: String = "this is the default value" - assertEquals("hello", Observable("hello").firstOrElse(mustNotBeCalled).toBlockingObservable.single) - assertEquals("this is the default value", Observable().firstOrElse(mustBeCalled).toBlockingObservable.single) - } - - @Test def testFirstOrElseWithError() { - val msg = "msg6251" - var receivedMsg = "none" - try { - Observable[Int](new Exception(msg)).firstOrElse(10).toBlockingObservable.single - } catch { - case e: Exception => receivedMsg = e.getCause().getMessage() - } - assertEquals(receivedMsg, msg) - } - - /* - @Test def testHead() { - val observer = mock(classOf[Observer[Int]]) - val o = Observable().head - val sub = o.subscribe(observer) - verify(observer, never).onNext(any(classOf[Int])) - verify(observer, never).onCompleted() - verify(observer, times(1)).onError(any(classOf[NoSuchElementException])) - } - */ - - @Test def testTest() = { - val a: Observable[Int] = Observable() - assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) - println("This UnitTestSuite.testTest() for rx.lang.scala.Observable") - } - -} + + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/Observer.scala b/language-adaptors/rxjava-scala/src/main/scala/Observer.scala new file mode 100644 index 0000000000..f66ea52c57 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/Observer.scala @@ -0,0 +1,47 @@ +package rx.lang.scala + +/** + Provides a mechanism for receiving push-based notifications. +* +* After an Observer calls an [[rx.lang.scala.Observable]]'s `subscribe` method, the Observable +* calls the Observer's `onNext` method to provide notifications. A well-behaved Observable will +* call an Observer's `onCompleted` method exactly once or the Observer's `onError` method exactly once. +*/ +trait Observer[-T] { + + def asJavaObserver: rx.Observer[_ >: T] + + /** + * Provides the Observer with new data. + * + * The [[rx.lang.scala.Observable]] calls this closure 0 or more times. + * + * The [[rx.lang.scala.Observable]] will not call this method again after it calls either `onCompleted` or `onError`. + */ + def onNext(value: T): Unit = asJavaObserver.onNext(value) + + /** + * Notifies the Observer that the [[rx.lang.scala.Observable]] has experienced an error condition. + * + * If the [[rx.lang.scala.Observable]] calls this method, it will not thereafter call `onNext` or `onCompleted`. + */ + def onError(error: Throwable): Unit = asJavaObserver.onError(error) + + /** + * Notifies the Observer that the [[rx.lang.scala.Observable]] has finished sending push-based notifications. + * + * The [[rx.lang.scala.Observable]] will not call this method if it calls `onError`. + */ + def onCompleted(): Unit = asJavaObserver.onCompleted() + +} + +object Observer { + def apply[T](observer: rx.Observer[T]) : Observer[T] = { + new Observer[T]() { + def asJavaObserver: rx.Observer[_ >: T] = observer + } + } +} + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala new file mode 100644 index 0000000000..10e5bc0d15 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala @@ -0,0 +1,212 @@ +package rx.lang.scala + +import java.util.Date +import scala.concurrent.duration.Duration +import scala.language.postfixOps +import ImplicitFunctionConversions.scalaFunction0ProducingUnitToAction0 +import ImplicitFunctionConversions.schedulerActionToFunc2 +import rx.util.functions.{Action0, Action1, Func2} +import rx.lang.scala.subscriptions.Subscription + +/** + * Represents an object that schedules units of work. + */ +trait Scheduler { + def asJavaScheduler: rx.Scheduler + + /** + * Schedules a cancelable action to be executed. + * + * @param action Action to schedule. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(action: rx.lang.scala.Scheduler => Subscription): Subscription = { + this.schedule[Integer](0, (s: Scheduler, x: Integer) => action(s): Subscription): Subscription + } + + /** + * Schedules a cancelable action to be executed. + * + * @param state State to pass into the action. + * @param action Action to schedule. + * @return a subscription to be able to unsubscribe from action. + */ + private def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription = { + Subscription(asJavaScheduler.schedule(state, new Func2[rx.Scheduler, T, rx.Subscription] { + def call(t1: rx.Scheduler, t2: T): rx.Subscription = { + action(Scheduler(t1), t2).asJavaSubscription + } + })) + } + + /** + * Schedules a cancelable action to be executed in delayTime. + * + * @param action Action to schedule. + * @param delayTime Time the action is to be delayed before executing. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(delayTime: Duration)(action: Scheduler => Subscription): Subscription = { + this.schedule[Integer](0, (s: Scheduler, x: Integer) => action(s), delayTime: Duration): Subscription + } + + /** + * Schedules a cancelable action to be executed in delayTime. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @param delayTime + * Time the action is to be delayed before executing. + * @return a subscription to be able to unsubscribe from action. + */ + private def schedule[T](state: T, action: (Scheduler, T) => Subscription, delayTime: Duration): Subscription = { + Subscription(asJavaScheduler.schedule(state, action, delayTime.length, delayTime.unit)) + } + + /** + * Schedules a cancelable action to be executed periodically. + * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing + * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. + * + * @param action The action to execute periodically. + * @param initialDelay Time to wait before executing the action for the first time. + * @param period The time interval to wait each time in between executing the action. + * @return A subscription to be able to unsubscribe from action. + */ + def schedule(initialDelay: Duration, period: Duration)(action: Scheduler => Subscription): Subscription = { + this.schedulePeriodically[Integer](0, (s: Scheduler, x:Integer) => action(s): Subscription, initialDelay: Duration, period: Duration): Subscription + } + + /** + * Schedules a cancelable action to be executed periodically. + * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing + * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. + * + * @param state + * State to pass into the action. + * @param action + * The action to execute periodically. + * @param initialDelay + * Time to wait before executing the action for the first time. + * @param period + * The time interval to wait each time in between executing the action. + * @return A subscription to be able to unsubscribe from action. + */ + private def schedulePeriodically[T](state: T, action: (Scheduler, T) => Subscription, initialDelay: Duration, period: Duration): Subscription = { + Subscription(asJavaScheduler.schedulePeriodically(state, action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit)) + } + + /** + * Schedules a cancelable action to be executed at dueTime. + * + * @param action Action to schedule. + * @param dueTime Time the action is to be executed. If in the past it will be executed immediately. + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(dueTime: Date)(action: Scheduler => Subscription): Subscription = { + this.schedule(0: Integer, (s: Scheduler, x: Integer) => action(s): Subscription, dueTime: Date): Subscription + } + + /** + * Schedules a cancelable action to be executed at dueTime. + * + * @param state + * State to pass into the action. + * @param action + * Action to schedule. + * @param dueTime + * Time the action is to be executed. If in the past it will be executed immediately. + * @return a subscription to be able to unsubscribe from action. + */ + private def schedule[T](state: T, action: (Scheduler, T) => Subscription, dueTime: Date): Subscription = { + Subscription(asJavaScheduler.schedule(state, action, dueTime)) + } + + /** + * Schedules an action to be executed. + * + * @param action + * action + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(action: =>Unit): Subscription = { + Subscription(asJavaScheduler.schedule(()=>action)) + } + + /** + * Schedules an action to be executed in delayTime. + * + * @param action action + * @return a subscription to be able to unsubscribe from action. + */ + def schedule(delayTime: Duration)(action: =>Unit): Subscription = { + Subscription(asJavaScheduler.schedule(()=>action, delayTime.length, delayTime.unit)) + } + + /** + * Schedules an action to be executed periodically. + * + * @param action + * The action to execute periodically. + * @param initialDelay + * Time to wait before executing the action for the first time. + * @param period + * The time interval to wait each time in between executing the action. + * @return A subscription to be able to unsubscribe from action. + */ + def schedule(initialDelay: Duration, period: Duration)(action: =>Unit): Subscription = { + Subscription(asJavaScheduler.schedulePeriodically(()=>action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit)) + } + + def scheduleRec(work: (=>Unit)=>Unit): Subscription = { + Subscription(asJavaScheduler.schedule(new Action1[Action0] { + def call(t1: Action0){ + work{ t1 } + } + })) + //action1[action0] + +// val subscription = new rx.subscriptions.MultipleAssignmentSubscription() +// +// subscription.setSubscription( +// this.schedule(scheduler => { +// def loop(): Unit = subscription.setSubscription(scheduler.schedule{ work{ loop() }}) +// loop() +// subscription +// })) +// subscription + } + + /** + * Returns the scheduler's notion of current absolute time in milliseconds. + */ + def now: Long = { + asJavaScheduler.now + } + + /** + * Parallelism available to a Scheduler. + * + * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. + * + * @return the scheduler's available degree of parallelism. + */ + def degreeOfParallelism: Int = { + asJavaScheduler.degreeOfParallelism + } + +} + +/** + * Provides constructors for Schedulers. + */ +object Scheduler { + private class WrapJavaScheduler(val asJavaScheduler: rx.Scheduler) extends Scheduler + + /** + * Constructs a Scala Scheduler from a Java Scheduler. + */ + def apply(s: rx.Scheduler): Scheduler = new WrapJavaScheduler(s) +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala b/language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala new file mode 100644 index 0000000000..9b36d3ca74 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala @@ -0,0 +1,23 @@ +package rx.lang.scala + +// Cannot yet have inner class because of this error message: +// "implementation restriction: nested class is not allowed in value class. +// This restriction is planned to be removed in subsequent releases." +private[scala] class WithFilter[+T] (p: T => Boolean, asJava: rx.Observable[_ <: T]) { + + import ImplicitFunctionConversions._ + + def map[B](f: T => B): Observable[B] = { + Observable[B](asJava.filter(p).map[B](f)) + } + + def flatMap[B](f: T => Observable[B]): Observable[B] = { + Observable[B](asJava.filter(p).flatMap[B]((x: T) => f(x).asJavaObservable)) + } + + def withFilter(q: T => Boolean): Observable[T] = { + Observable[T](asJava.filter((x: T) => p(x) && q(x))) + } + + // there is no foreach here, that's only available on BlockingObservable +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala b/language-adaptors/rxjava-scala/src/main/scala/concurrency/Schedulers.scala similarity index 80% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala rename to language-adaptors/rxjava-scala/src/main/scala/concurrency/Schedulers.scala index 960df660d4..8ba88ba2d0 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/concurrency/Schedulers.scala @@ -1,18 +1,3 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package rx.lang.scala.concurrency import java.util.concurrent.Executor diff --git a/language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala new file mode 100644 index 0000000000..a76d95f096 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala @@ -0,0 +1,105 @@ +package rx.lang.scala.concurrency + +import scala.concurrent.duration.Duration +import rx.lang.scala.Scheduler +//import org.scalatest.junit.JUnitSuite + +/** + * Scheduler with artificial time, useful for testing. + * + * For example, you could test the `Observable.interval` operation using a `TestScheduler` as follows: + * + * {{{ + * @Test def testInterval() { + * import org.mockito.Matchers._ + * import org.mockito.Mockito._ + * + * val scheduler = TestScheduler() + * val observer = mock(classOf[rx.Observer[Long]]) + * + * val o = Observable.interval(1 second, scheduler) + * val sub = o.subscribe(observer) + * + * verify(observer, never).onNext(0L) + * verify(observer, never).onCompleted() + * verify(observer, never).onError(any(classOf[Throwable])) + * + * scheduler.advanceTimeTo(2 seconds) + * + * val inOrdr = inOrder(observer); + * inOrdr.verify(observer, times(1)).onNext(0L) + * inOrdr.verify(observer, times(1)).onNext(1L) + * inOrdr.verify(observer, never).onNext(2L) + * verify(observer, never).onCompleted() + * verify(observer, never).onError(any(classOf[Throwable])) + * + * sub.unsubscribe(); + * scheduler.advanceTimeTo(4 seconds) + * verify(observer, never).onNext(2L) + * verify(observer, times(1)).onCompleted() + * verify(observer, never).onError(any(classOf[Throwable])) + * } + * }}} + */ +class TestScheduler extends Scheduler { + val asJavaScheduler = new rx.concurrency.TestScheduler + + def advanceTimeBy(time: Duration) { + asJavaScheduler.advanceTimeBy(time.length, time.unit) + } + + def advanceTimeTo(time: Duration) { + asJavaScheduler.advanceTimeTo(time.length, time.unit) + } + + def triggerActions() { + asJavaScheduler.triggerActions() + } +} + +/** + * Provides constructors for `TestScheduler`. + */ +object TestScheduler { + def apply(): TestScheduler = { + new TestScheduler + } +} + +//private class UnitTest extends JUnitSuite { +// import org.junit.Test +// import scala.concurrent.duration._ +// import scala.language.postfixOps +// import rx.lang.scala.{Observable, Observer} +// +// @Test def testInterval() { +// import org.mockito.Matchers._ +// import org.mockito.Mockito._ +// +// val scheduler = TestScheduler() +// val observer = mock(classOf[rx.Observer[Long]]) +// +// val o = Observable.interval(1 second, scheduler) +// val sub = o.subscribe(observer) +// +// verify(observer, never).onNext(0L) +// verify(observer, never).onCompleted() +// verify(observer, never).onError(any(classOf[Throwable])) +// +// scheduler.advanceTimeTo(2 seconds) +// +// val inOrdr = inOrder(observer); +// inOrdr.verify(observer, times(1)).onNext(0L) +// inOrdr.verify(observer, times(1)).onNext(1L) +// inOrdr.verify(observer, never).onNext(2L) +// verify(observer, never).onCompleted() +// verify(observer, never).onError(any(classOf[Throwable])) +// +// sub.unsubscribe(); +// scheduler.advanceTimeTo(4 seconds) +// verify(observer, never).onNext(2L) +// verify(observer, times(1)).onCompleted() +// verify(observer, never).onError(any(classOf[Throwable])) +// } +//} + diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/observables/BlockingObservable.scala similarity index 95% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala rename to language-adaptors/rxjava-scala/src/main/scala/observables/BlockingObservable.scala index 8d9323ba32..f9e98efa63 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/observables/BlockingObservable.scala @@ -21,7 +21,7 @@ import rx.lang.scala.ImplicitFunctionConversions._ /** * An Observable that provides blocking operators. * - * You can obtain a BlockingObservable from an Observable using [[Observable.toBlockingObservable]] + * You can obtain a BlockingObservable from an Observable using [[rx.lang.scala.Observable.toBlockingObservable]] */ // constructor is private because users should use Observable.toBlockingObservable class BlockingObservable[+T] private[scala] (val asJava: rx.observables.BlockingObservable[_ <: T]) @@ -39,13 +39,13 @@ class BlockingObservable[+T] private[scala] (val asJava: rx.observables.Blocking * * * - * @param onNext + * @param f * the {@link Action1} to invoke for every item emitted by the {@link Observable} * @throws RuntimeException * if an error occurs */ def foreach(f: T => Unit): Unit = { - asJava.forEach(f); + asJava.forEach(f) } def withFilter(p: T => Boolean): WithFilter[T] = { @@ -117,14 +117,14 @@ class BlockingObservable[+T] private[scala] (val asJava: rx.observables.Blocking * Returns an {@link Iterator} that iterates over all items emitted by this {@link Observable}. */ def toIterable: Iterable[T] = { - asJava.toIterable().asScala: Iterable[T] // useless ascription because of compiler bug + asJava.toIterable.asScala: Iterable[T] // useless ascription because of compiler bug } /** * Returns a {@link List} that contains all items emitted by this {@link Observable}. */ def toList: List[T] = { - asJava.toIterable().asScala.toList: List[T] // useless ascription because of compiler bug + asJava.toIterable.asScala.toList: List[T] // useless ascription because of compiler bug } } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala deleted file mode 100644 index 1165bd4620..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.scala - -import java.util.Date - -import scala.concurrent.duration.Duration -import scala.concurrent.duration.DurationInt -import scala.language.postfixOps - -import org.junit.Before -import org.junit.Test -import org.mockito.Matchers.any -import org.mockito.Mockito.inOrder -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.scalatest.junit.JUnitSuite - -import rx.lang.scala.ImplicitFunctionConversions.scalaFunction0ProducingUnitToAction0 -import rx.lang.scala.ImplicitFunctionConversions.schedulerActionToFunc2 -import rx.lang.scala.concurrency.TestScheduler - - -/** - * Represents an object that schedules units of work. - */ -trait Scheduler { - def asJava: rx.Scheduler - - /** - * Schedules a cancelable action to be executed. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription): Subscription = { - asJava.schedule(state, action) - } - - /** - * Schedules a cancelable action to be executed in delayTime. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @param delayTime - * Time the action is to be delayed before executing. - * @param unit - * Time unit of the delay time. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription, delayTime: Duration): Subscription = { - asJava.schedule(state, action, delayTime.length, delayTime.unit) - } - - /** - * Schedules a cancelable action to be executed periodically. - * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing - * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this. - * - * @param state - * State to pass into the action. - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @return A subscription to be able to unsubscribe from action. - */ - def schedulePeriodically[T](state: T, action: (Scheduler, T) => Subscription, initialDelay: Duration, period: Duration): Subscription = { - asJava.schedulePeriodically(state, action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit) - } - - /** - * Schedules a cancelable action to be executed at dueTime. - * - * @param state - * State to pass into the action. - * @param action - * Action to schedule. - * @param dueTime - * Time the action is to be executed. If in the past it will be executed immediately. - * @return a subscription to be able to unsubscribe from action. - */ - def schedule[T](state: T, action: (Scheduler, T) => Subscription, dueTime: Date): Subscription = { - asJava.schedule(state, action, dueTime) - } - - /** - * Schedules an action to be executed. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - def schedule(action: () => Unit): Subscription = { - asJava.schedule(action) - } - - /** - * Schedules an action to be executed in delayTime. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - def schedule(action: () => Unit, delayTime: Duration): Subscription = { - asJava.schedule(action, delayTime.length, delayTime.unit) - } - - /** - * Schedules an action to be executed periodically. - * - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @return A subscription to be able to unsubscribe from action. - */ - def schedulePeriodically(action: () => Unit, initialDelay: Duration, period: Duration): Subscription = { - asJava.schedulePeriodically(action, initialDelay.length, initialDelay.unit.convert(period.length, period.unit), initialDelay.unit) - } - - /** - * Returns the scheduler's notion of current absolute time in milliseconds. - */ - def now: Long = { - asJava.now - } - - /** - * Parallelism available to a Scheduler. - * - * This defaults to {@code Runtime.getRuntime().availableProcessors()} but can be overridden for use cases such as scheduling work on a computer cluster. - * - * @return the scheduler's available degree of parallelism. - */ - def degreeOfParallelism: Int = { - asJava.degreeOfParallelism - } - -} - -/** - * Provides constructors for Schedulers. - */ -object Scheduler { - private class WrapJavaScheduler(val asJava: rx.Scheduler) extends Scheduler - - /** - * Constructs a Scala Scheduler from a Java Scheduler. - */ - def apply(s: rx.Scheduler): Scheduler = new WrapJavaScheduler(s) -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala deleted file mode 100644 index a8090a887e..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.scala.concurrency - -import scala.concurrent.duration.Duration -import rx.lang.scala.Scheduler -import org.scalatest.junit.JUnitSuite - -/** - * Scheduler with artificial time, useful for testing. - * - * For example, you could test the `Observable.interval` operation using a `TestScheduler` as follows: - * - * {{{ - * @Test def testInterval() { - * import org.mockito.Matchers._ - * import org.mockito.Mockito._ - * - * val scheduler = TestScheduler() - * val observer = mock(classOf[rx.Observer[Long]]) - * - * val o = Observable.interval(1 second, scheduler) - * val sub = o.subscribe(observer) - * - * verify(observer, never).onNext(0L) - * verify(observer, never).onCompleted() - * verify(observer, never).onError(any(classOf[Throwable])) - * - * scheduler.advanceTimeTo(2 seconds) - * - * val inOrdr = inOrder(observer); - * inOrdr.verify(observer, times(1)).onNext(0L) - * inOrdr.verify(observer, times(1)).onNext(1L) - * inOrdr.verify(observer, never).onNext(2L) - * verify(observer, never).onCompleted() - * verify(observer, never).onError(any(classOf[Throwable])) - * - * sub.unsubscribe(); - * scheduler.advanceTimeTo(4 seconds) - * verify(observer, never).onNext(2L) - * verify(observer, times(1)).onCompleted() - * verify(observer, never).onError(any(classOf[Throwable])) - * } - * }}} - */ -class TestScheduler extends Scheduler { - val asJava = new rx.concurrency.TestScheduler - - def advanceTimeBy(time: Duration) { - asJava.advanceTimeBy(time.length, time.unit) - } - - def advanceTimeTo(time: Duration) { - asJava.advanceTimeTo(time.length, time.unit) - } - - def triggerActions() { - asJava.triggerActions() - } -} - -/** - * Provides constructors for `TestScheduler`. - */ -object TestScheduler { - def apply(): TestScheduler = { - new TestScheduler - } -} - -private class UnitTest extends JUnitSuite { - import org.junit.Test - import scala.concurrent.duration._ - import scala.language.postfixOps - import rx.lang.scala.{Observable, Observer} - - @Test def testInterval() { - import org.mockito.Matchers._ - import org.mockito.Mockito._ - - val scheduler = TestScheduler() - val observer = mock(classOf[rx.Observer[Long]]) - - val o = Observable.interval(1 second, scheduler) - val sub = o.subscribe(observer) - - verify(observer, never).onNext(0L) - verify(observer, never).onCompleted() - verify(observer, never).onError(any(classOf[Throwable])) - - scheduler.advanceTimeTo(2 seconds) - - val inOrdr = inOrder(observer); - inOrdr.verify(observer, times(1)).onNext(0L) - inOrdr.verify(observer, times(1)).onNext(1L) - inOrdr.verify(observer, never).onNext(2L) - verify(observer, never).onCompleted() - verify(observer, never).onError(any(classOf[Throwable])) - - sub.unsubscribe(); - scheduler.advanceTimeTo(4 seconds) - verify(observer, never).onNext(2L) - verify(observer, times(1)).onCompleted() - verify(observer, never).onError(any(classOf[Throwable])) - } -} - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala deleted file mode 100644 index a3e61c0021..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/package.scala +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.scala - -import rx.concurrency.CurrentThreadScheduler - -/** - * Provides schedulers. - */ -package object concurrency { - - // These classes are not exposed to Scala users, but are accessible through rx.lang.scala.concurrency.Schedulers: - - // rx.concurrency.CurrentThreadScheduler - // rx.concurrency.ExecutorScheduler - // rx.concurrency.ImmediateScheduler - // rx.concurrency.NewThreadScheduler -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala deleted file mode 100644 index 2b43860b53..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.scala - -/** - * Contains special Observables. - * - * In Scala, this package only contains [[BlockingObservable]]. - * In the corresponding Java package `rx.observables`, there is also a - * `GroupedObservable` and a `ConnectableObservable`, but these are not needed - * in Scala, because we use a pair `(key, observable)` instead of `GroupedObservable` - * and a pair `(startFunction, observable)` instead of `ConnectableObservable`. - */ -package object observables {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala deleted file mode 100644 index 8aa0e63760..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang - -import java.util.concurrent.TimeUnit -import java.util.Date - -/* - * Note that: - * - Scala users cannot use Java's types with variance without always using writing - * e.g. rx.Notification[_ <: T], so we create aliases fixing the variance - * - For consistency, we create aliases for all types which Scala users need - */ - -/** - * This package contains all classes that RxScala users need. - * - * It mirrors the structure of package `rx`, but implementation classes that RxScala users - * will not need are left out. - */ -package object scala { - - /* - * Here we're imitating C's preprocessor using Search & Replace. - * - * To activate the code needed to get nice Scaladoc, do the following replacements: - * /*//#ifdef SCALADOC --> //#ifdef SCALADOC - * *///#else --> /*//#else - * //#endif --> *///#endif - * - * To get back to the actual code, undo the above replacements. - * - */ - - /*//#ifdef SCALADOC - - /** - * Provides a mechanism for receiving push-based notifications. - * - * After an Observer calls an [[Observable]]'s `subscribe` method, the Observable - * calls the Observer's `onNext` method to provide notifications. A well-behaved Observable will - * call an Observer's `onCompleted` method exactly once or the Observer's `onError` method exactly once. - */ - trait Observer[-T] { - - /** - * Notifies the Observer that the [[Observable]] has finished sending push-based notifications. - * - * The [[Observable]] will not call this method if it calls `onError`. - */ - def onCompleted(): Unit - - /** - * Notifies the Observer that the [[Observable]] has experienced an error condition. - * - * If the [[Observable]] calls this method, it will not thereafter call `onNext` or `onCompleted`. - */ - def onError(e: Throwable): Unit - - /** - * Provides the Observer with new data. - * - * The [[Observable]] calls this closure 0 or more times. - * - * The [[Observable]] will not call this method again after it calls either `onCompleted` or `onError`. - */ - def onNext(arg: T): Unit - } - - /** - * Subscriptions are returned from all `Observable.subscribe` methods to allow unsubscribing. - * - * This interface is the equivalent of `IDisposable` in the .NET Rx implementation. - */ - trait Subscription { - /** - * Call this method to stop receiving notifications on the Observer that was registered when - * this Subscription was received. - */ - def unsubscribe(): Unit - } - - import language.implicitConversions - - private[scala] implicit def fakeSubscription2RxSubscription(s: Subscription): rx.Subscription = - new rx.Subscription { - def unsubscribe() = s.unsubscribe() - } - private[scala] implicit def rxSubscription2FakeSubscription(s: rx.Subscription): Subscription = - new Subscription { - def unsubscribe() = s.unsubscribe() - } - - private[scala] implicit def schedulerActionToFunc2[T](action: (Scheduler, T) => Subscription) = - new rx.util.functions.Func2[rx.Scheduler, T, rx.Subscription] { - def call(s: rx.Scheduler, t: T): rx.Subscription = { - action(ImplicitFunctionConversions.javaSchedulerToScalaScheduler(s), t) - } - } - - private[scala] implicit def fakeObserver2RxObserver[T](o: Observer[T]): rx.Observer[_ >: T] = ??? - private[scala] implicit def rxObserver2fakeObserver[T](o: rx.Observer[_ >: T]): Observer[T] = ??? - - *///#else - - type Observer[-T] = rx.Observer[_ >: T] - - type Subscription = rx.Subscription - - //#endif - - /** - * Allows to construct observables in a similar way as futures. - * - * Example: - * - * {{{ - * implicit val scheduler = Schedulers.threadPoolForIO - * val o: Observable[List[Friend]] = observable { - * session.getFriends - * } - * o.subscribe( - * friendList => println(friendList), - * err => println(err.getMessage) - * ) - * }}} - */ - def observable[T](body: => T)(implicit scheduler: Scheduler): Observable[T] = { - Observable(1).observeOn(scheduler).map(_ => body) - } -} - -/* - -These classes are considered unnecessary for Scala users, so we don't create aliases for them: - -rx.plugins.RxJavaErrorHandler -rx.plugins.RxJavaObservableExecutionHook -rx.plugins.RxJavaPlugins - -rx.subscriptions.BooleanSubscription -rx.subscriptions.CompositeSubscription -rx.subscriptions.Subscriptions - -*/ diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala deleted file mode 100644 index 07076772f5..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.scala - -/** - * Provides the type `Subject`. - */ -package object subjects { - - /** - * A Subject is an Observable and an Observer at the same time. - * - * The Java Subject looks like this: - * {{{ - * public abstract class Subject extends Observable implements Observer - * }}} - */ - type Subject[-T, +R] = rx.subjects.Subject[_ >: T, _ <: R] - - // For nicer scaladoc, we would like to present something like this: - /* - trait Observable[+R] {} - trait Observer[-T] {} - trait Subject[-T, +R] extends Observable[R] with Observer[T] { } - */ - - // We don't make aliases to these types, because they are considered internal/not needed by users: - // rx.subjects.AsyncSubject - // rx.subjects.BehaviorSubject - // rx.subjects.PublishSubject - // rx.subjects.ReplaySubject - -} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala new file mode 100644 index 0000000000..ad23ce841c --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala @@ -0,0 +1,11 @@ +package rx.lang.scala.subjects + +import rx.lang.scala.Subject + +object AsyncSubject { + def apply[T](): AsyncSubject[T] = { + new AsyncSubject[T](rx.subjects.AsyncSubject.create()) + } +} + +class AsyncSubject[T] private[scala] (val asJavaSubject: rx.subjects.AsyncSubject[T]) extends Subject[T,T] {} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala new file mode 100644 index 0000000000..fdf873f096 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala @@ -0,0 +1,15 @@ +package rx.lang.scala.subjects + +import rx.lang.scala.Subject + +object BehaviorSubject { + def apply[T](value: T): BehaviorSubject[T] = { + new BehaviorSubject[T](rx.subjects.BehaviorSubject.createWithDefaultValue(value)) + } +} + +class BehaviorSubject[T] private[scala] (val asJavaSubject: rx.subjects.BehaviorSubject[T]) extends Subject[T,T] {} + + + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala new file mode 100644 index 0000000000..93989bfe32 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala @@ -0,0 +1,12 @@ +package rx.lang.scala.subjects + +import rx.lang.scala.Subject + +object PublishSubject { + def apply[T](value: T): PublishSubject[T] = { + new PublishSubject[T](rx.subjects.PublishSubject.create()) + } +} + +class PublishSubject[T] private[scala] (val asJavaSubject: rx.subjects.PublishSubject[T]) extends Subject[T,T] { + } diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala b/language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala new file mode 100644 index 0000000000..6d4698e163 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala @@ -0,0 +1,15 @@ +package rx.lang.scala.subjects + +import rx.lang.scala.Subject + +object ReplaySubject { + def apply[T](): ReplaySubject[T] = { + new ReplaySubject[T](rx.subjects.ReplaySubject.create()) + } +} + +class ReplaySubject[T] private[scala] (val asJavaSubject: rx.subjects.ReplaySubject[T]) extends Subject[T,T] { +} + + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala b/language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala new file mode 100644 index 0000000000..cb92df90d9 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala @@ -0,0 +1,11 @@ +package rx.lang.scala + +/** +* A Subject is an Observable and an Observer at the same time. +*/ +trait Subject[-T, +R] extends Observable[R] with Observer[T] { + val asJavaSubject: rx.subjects.Subject[_ >: T, _<: R] + def asJavaObservable: rx.Observable[_ <: R] = asJavaSubject + def asJavaObserver: rx.Observer[_ >: T] = asJavaSubject +} + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala new file mode 100644 index 0000000000..246c68db21 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala @@ -0,0 +1,38 @@ +package rx.lang.scala.subscriptions + +import rx.lang.scala._ + +object BooleanSubscription { + + /** + * Creates a [[rx.lang.scala.subscriptions.BooleanSubscription]]. + */ + def apply(): BooleanSubscription = { + new BooleanSubscription(new rx.subscriptions.BooleanSubscription()) + } + + /** + * Creates a [[rx.lang.scala.subscriptions.BooleanSubscription]] that invokes the specified action when unsubscribed. + */ + def apply(u: => Unit): BooleanSubscription = { + new BooleanSubscription(new rx.subscriptions.BooleanSubscription { + override def unsubscribe(): Unit = { + u + super.unsubscribe() + } + }) + } +} + +/** + * Represents a [[rx.lang.scala.Subscription]] that can be checked for status. + */ +class BooleanSubscription private[scala] (val asJavaSubscription: rx.subscriptions.BooleanSubscription) + extends Subscription { + + /** + * Checks whether the subscription has been unsubscribed. + */ + def isUnsubscribed: Boolean = asJavaSubscription.isUnsubscribed + +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala new file mode 100644 index 0000000000..6437013a4f --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala @@ -0,0 +1,61 @@ +package rx.lang.scala.subscriptions + +import rx.lang.scala._ + +object CompositeSubscription { + + /** + * Creates a [[rx.lang.scala.subscriptions.CompositeSubscription]] from a group of [[rx.lang.scala.Subscription]]. + */ + def apply(subscriptions: Subscription*): CompositeSubscription = { + new CompositeSubscription(new rx.subscriptions.CompositeSubscription(subscriptions.map(_.asJavaSubscription).toArray : _*)) + } + + /** + * Creates a [[rx.lang.scala.subscriptions.CompositeSubscription]]. + */ + def apply(): CompositeSubscription = { + new CompositeSubscription(new rx.subscriptions.CompositeSubscription()) + } + + /** + * Creates a [[rx.lang.scala.subscriptions.CompositeSubscription]]. + */ + def apply(subscription: rx.subscriptions.CompositeSubscription): CompositeSubscription = { + new CompositeSubscription(subscription) + } +} + +/** + * Represents a group of [[rx.lang.scala.Subscription]] that are disposed together. + */ +class CompositeSubscription private[scala] (val asJavaSubscription: rx.subscriptions.CompositeSubscription) + extends Subscription +{ + /** + * Adds a subscription to the group, + * or unsubscribes immediately is the [[rx.subscriptions.CompositeSubscription]] is unsubscribed. + * @param subscription the subscription to be added. + * @return the [[rx.subscriptions.CompositeSubscription]] itself. + */ + def +=(subscription: Subscription): this.type = { + asJavaSubscription.add(subscription.asJavaSubscription) + this + } + + /** + * Removes and unsubscribes a subscription to the group, + * @param subscription the subscription be removed. + * @return the [[rx.subscriptions.CompositeSubscription]] itself. + */ + def -=(subscription: Subscription): this.type = { + asJavaSubscription.remove(subscription.asJavaSubscription) + this + } + + /** + * Checks whether the subscription has been unsubscribed. + */ + def isUnsubscribed: Boolean = asJavaSubscription.isUnsubscribed + +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala new file mode 100644 index 0000000000..1cdd485d37 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala @@ -0,0 +1,54 @@ +package rx.lang.scala.subscriptions + +import rx.lang.scala._ + +object MultipleAssignmentSubscription { + + /** + * Creates a [[rx.lang.scala.subscriptions.MultipleAssignmentSubscription]] that invokes the specified action when unsubscribed. + */ + def apply(subscription: => Unit): MultipleAssignmentSubscription = { + val m = MultipleAssignmentSubscription() + m.subscription = Subscription{ subscription } + m + } + + /** + * Creates a [[rx.lang.scala.subscriptions.MultipleAssignmentSubscription]]. + */ + def apply(): MultipleAssignmentSubscription = { + new MultipleAssignmentSubscription(new rx.subscriptions.MultipleAssignmentSubscription()) + } +} + + + +/** + * Represents a [[rx.lang.scala.subscriptions.Subscription]] whose underlying subscription can be swapped for another subscription. + */ +class MultipleAssignmentSubscription private[scala] (val asJavaSubscription: rx.subscriptions.MultipleAssignmentSubscription) + extends Subscription { + + /** + * Gets the underlying subscription. + */ + def subscription: Subscription = Subscription(asJavaSubscription.getSubscription) + + /** + * Gets the underlying subscription + * @param that the new subscription + * @return the [[rx.lang.scala.subscriptions.MultipleAssignmentSubscription]] itself. + */ + def subscription_=(that: Subscription): this.type = { + asJavaSubscription.setSubscription(that.asJavaSubscription) + this + } + + /** + * Checks whether the subscription has been unsubscribed. + */ + def isUnsubscribed: Boolean = asJavaSubscription.isUnsubscribed + +} + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala new file mode 100644 index 0000000000..8ca4083c8d --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala @@ -0,0 +1,48 @@ +package rx.lang.scala.subscriptions + +import rx.lang.scala.Subscription +import java.util.concurrent.atomic.AtomicBoolean + + +object SerialSubscription { + + /** + * Creates a [[rx.lang.scala.subscriptions.SerialSubscription]]. + */ + def apply(): SerialSubscription = { + new SerialSubscription(new rx.subscriptions.SerialSubscription()) + } + + /** + * Creates a [[rx.lang.scala.subscriptions.SerialSubscription]] that invokes the specified action when unsubscribed. + */ + def apply(unsubscribe: => Unit): SerialSubscription = { + val s= SerialSubscription() + s.subscription = Subscription{ unsubscribe } + s + } +} + +/** + * Represents a [[rx.lang.scala.Subscription]] that can be checked for status. + */ +class SerialSubscription private[scala] (val asJavaSubscription: rx.subscriptions.SerialSubscription) + extends Subscription { + + private val _isUnsubscribed = new AtomicBoolean(false) + + /** + * Checks whether the subscription has been unsubscribed. + */ + def isUnsubscribed: Boolean = _isUnsubscribed.get() + + /** + * Unsubscribes this subscription, setting isUnsubscribed to true. + */ + override def unsubscribe(): Unit = { super.unsubscribe(); _isUnsubscribed.set(true) } + + def subscription_=(value: Subscription): Unit = asJavaSubscription.setSubscription(value.asJavaSubscription) + def subscription: Subscription = Subscription(asJavaSubscription.getSubscription) + +} + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala new file mode 100644 index 0000000000..ec4e7b5704 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala @@ -0,0 +1,83 @@ +/** + * Copyright 2013 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rx.lang.scala { + + /** + * Subscriptions are returned from all `Observable.subscribe` methods to allow unsubscribing. + * + * This interface is the equivalent of `IDisposable` in the .NET Rx implementation. + */ + trait Subscription { + + val asJavaSubscription: rx.Subscription + + /** + * Call this method to stop receiving notifications on the Observer that was registered when + * this Subscription was received. + */ + def unsubscribe(): Unit = asJavaSubscription.unsubscribe() + + /** + * Checks if the subscription is unsubscribed. + */ + def isUnsubscribed: Boolean + } +} + +package rx.lang.scala.subscriptions { + +import rx.lang.scala.Subscription +import java.util.concurrent.atomic.AtomicBoolean + + +object Subscription { + + /** + * Creates an [[rx.lang.scala.Subscription]] from an[[rx.Subscription]]. + */ + def apply(subscription: rx.Subscription): Subscription = { + subscription match { + case x: rx.subscriptions.BooleanSubscription => new BooleanSubscription(x) + case x: rx.subscriptions.CompositeSubscription => new CompositeSubscription(x) + case x: rx.subscriptions.MultipleAssignmentSubscription => new MultipleAssignmentSubscription(x) + case x: rx.subscriptions.SerialSubscription => new SerialSubscription(x) + case x: rx.Subscription => Subscription { x.unsubscribe() } + } + } + + /** + * Creates an [[rx.lang.scala.Subscription]] that invokes the specified action when unsubscribed. + */ + def apply(u: => Unit): Subscription = { + new Subscription () { + + private val _isUnsubscribed = new AtomicBoolean(false) + def isUnsubscribed = _isUnsubscribed.get() + + val asJavaSubscription = new rx.Subscription { + def unsubscribe() { u; _isUnsubscribed.set(true) } + } + } + } + } +} + + + + + + diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala new file mode 100644 index 0000000000..b856d97fd0 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala @@ -0,0 +1,24 @@ +package rx.lang + +package object scala { + + /** + * Allows to construct observables in a similar way as futures. + * + * Example: + * + * {{{ + * implicit val scheduler = Schedulers.threadPoolForIO + * val o: Observable[List[Friend]] = observable { + * session.getFriends + * } + * o.subscribe( + * friendList => println(friendList), + * err => println(err.getMessage) + * ) + * }}} + */ + def observable[T](body: => T)(implicit scheduler: Scheduler): Observable[T] = { + Observable(1).observeOn(scheduler).map(_ => body) + } +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/util/util.scala similarity index 67% rename from language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala rename to language-adaptors/rxjava-scala/src/main/scala/util/util.scala index ed19d849ab..fe58b2eeb4 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/util/util.scala @@ -16,31 +16,31 @@ package rx.lang.scala /** - * Provides [[Opening]]s, [[Closing]]s, and [[Timestamped]]. + * Provides [[rx.lang.scala.util.Opening]]s, [[rx.lang.scala.util.Closing]]s, and [[rx.util.Timestamped]]. */ package object util { /** * Tagging interface for objects which can open buffers. - * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ type Opening = rx.util.Opening /** * Creates an object which can open buffers. - * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ def Opening() = rx.util.Openings.create() /** * Tagging interface for objects which can close buffers. - * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ type Closing = rx.util.Closing /** * Creates an object which can close buffers. - * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] + * @see [[rx.lang.scala.Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]] */ def Closing() = rx.util.Closings.create() diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala similarity index 91% rename from language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala rename to language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala index fe1747a1e6..2580d7297d 100644 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala @@ -1,12 +1,12 @@ /** * Copyright 2013 Netflix, Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,42 +24,42 @@ import org.junit.Assert._ import rx.lang.scala.concurrency.Schedulers import java.io.IOException -@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily +//@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { @Test def intervalExample() { val o = Observable.interval(200 millis).take(5) o.subscribe(n => println("n = " + n)) - + // need to wait here because otherwise JUnit kills the thread created by interval() waitFor(o) - + println("done") } - + def msTicks(start: Long, step: Long): Observable[Long] = { // will be easier once we have Observable.generate method Observable.interval(step millis) map (_ * step + start) } - + def prefixedTicks(start: Long, step: Long, prefix: String): Observable[String] = { msTicks(start, step).map(prefix + _) } - + @Test def testTicks() { val o = prefixedTicks(5000, 500, "t = ").take(5) o.subscribe(output(_)) waitFor(o) } - + @Test def testSwitch() { // We do not have ultimate precision: Sometimes, 747 gets through, sometimes not val o = Observable.interval(1000 millis).map(n => prefixedTicks(0, 249, s"Observable#$n: ")) - .switch.take(16) + .switch.take(16) o.subscribe(output(_)) waitFor(o) } - + @Test def testSwitchOnObservableOfInt() { // Correctly rejected with error // "Cannot prove that Observable[Int] <:< Observable[Observable[U]]" @@ -69,27 +69,27 @@ class RxScalaDemo extends JUnitSuite { @Test def testObservableComparison() { val first = Observable(10, 11, 12) val second = Observable(10, 11, 12) - + val b1 = (first zip second) map (p => p._1 == p._2) forall (b => b) - + val equality = (a: Any, b: Any) => a == b val b2 = (first zip second) map (p => equality(p._1, p._2)) forall (b => b) - + assertTrue(b1.toBlockingObservable.single) assertTrue(b2.toBlockingObservable.single) } - + @Test def testObservableComparisonWithForComprehension() { val first = Observable(10, 11, 12) val second = Observable(10, 11, 12) - + val booleans = for ((n1, n2) <- (first zip second)) yield (n1 == n2) - + val b1 = booleans.forall(_ == true) // without `== true`, b1 is assigned the forall function - + assertTrue(b1.toBlockingObservable.single) } - + @Test def testStartWithIsUnnecessary() { val before = Observable(-2, -1, 0) val source = Observable(1, 2, 3) @@ -103,150 +103,150 @@ class RxScalaDemo extends JUnitSuite { o.subscribe(output(_)) waitFor(o) } - + def myInterval(period: Long): Observable[String] = { Observable.interval(period.millis).map(n => s"Obs-$period emits $n") } - + @Test def flattenManyExample() { val o = Observable.interval(500 millis).map(n => myInterval((n+1)*100)) val stopper = Observable.interval(5 seconds) o.flatten.takeUntil(stopper).toBlockingObservable.foreach(println(_)) } - + @Test def fattenSomeExample() { // To merge some observables which are all known already: Observable( - Observable.interval(200 millis), - Observable.interval(400 millis), - Observable.interval(800 millis) + Observable.interval(200 millis), + Observable.interval(400 millis), + Observable.interval(800 millis) ).flatten.take(12).toBlockingObservable.foreach(println(_)) - } - + } + @Test def rangeAndBufferExample() { val o = Observable(1 to 18) o.buffer(5).subscribe((l: Seq[Int]) => println(l.mkString("[", ", ", "]"))) } - + @Test def windowExample() { // this will be nicer once we have zipWithIndex - (for ((o, i) <- Observable(1 to 18).window(5) zip Observable(0 until 4); n <- o) - yield s"Observable#$i emits $n") - .subscribe(output(_)) + (for ((o, i) <- Observable(1 to 18).window(5) zip Observable(0 until 4); n <- o) + yield s"Observable#$i emits $n") + .subscribe(output(_)) } - + @Test def testReduce() { assertEquals(10, Observable(1, 2, 3, 4).reduce(_ + _).toBlockingObservable.single) } - + @Test def testForeach() { val numbers = Observable.interval(200 millis).take(3) - + // foreach is not available on normal Observables: // for (n <- numbers) println(n+10) - + // but on BlockingObservable, it is: for (n <- numbers.toBlockingObservable) println(n+10) } - + @Test def testForComprehension() { val observables = Observable(Observable(1, 2, 3), Observable(10, 20, 30)) val squares = (for (o <- observables; i <- o if i % 2 == 0) yield i*i) assertEquals(squares.toBlockingObservable.toList, List(4, 100, 400, 900)) } - + @Test def testTwoSubscriptionsToOneInterval() { val o = Observable.interval(100 millis).take(8) o.subscribe( - i => println(s"${i}a (on thread #${Thread.currentThread().getId()})") + i => println(s"${i}a (on thread #${Thread.currentThread().getId()})") ) o.subscribe( - i => println(s"${i}b (on thread #${Thread.currentThread().getId()})") + i => println(s"${i}b (on thread #${Thread.currentThread().getId()})") ) waitFor(o) } - + @Test def schedulersExample() { val o = Observable.interval(100 millis).take(8) o.observeOn(Schedulers.newThread).subscribe( - i => println(s"${i}a (on thread #${Thread.currentThread().getId()})") + i => println(s"${i}a (on thread #${Thread.currentThread().getId()})") ) o.observeOn(Schedulers.newThread).subscribe( - i => println(s"${i}b (on thread #${Thread.currentThread().getId()})") + i => println(s"${i}b (on thread #${Thread.currentThread().getId()})") ) waitFor(o) } - + @Test def testGroupByThenFlatMap() { val m = Observable(1, 2, 3, 4) val g = m.groupBy(i => i % 2) val t = g.flatMap((p: (Int, Observable[Int])) => p._2) - assertEquals(List(1, 2, 3, 4), t.toBlockingObservable.toList) + assertEquals(List(1, 2, 3, 4), t.toBlockingObservable.toList) } - + @Test def testGroupByThenFlatMapByForComprehension() { val m = Observable(1, 2, 3, 4) val g = m.groupBy(i => i % 2) val t = for ((i, o) <- g; n <- o) yield n - assertEquals(List(1, 2, 3, 4), t.toBlockingObservable.toList) + assertEquals(List(1, 2, 3, 4), t.toBlockingObservable.toList) } - + @Test def testGroupByThenFlatMapByForComprehensionWithTiming() { val m = Observable.interval(100 millis).take(4) val g = m.groupBy(i => i % 2) val t = for ((i, o) <- g; n <- o) yield n - assertEquals(List(0, 1, 2, 3), t.toBlockingObservable.toList) + assertEquals(List(0, 1, 2, 3), t.toBlockingObservable.toList) } @Test def timingTest() { val firstOnly = false val numbersByModulo3 = Observable.interval(1000 millis).take(9).groupBy(_ % 3) - + (for ((modulo, numbers) <- numbersByModulo3) yield { println("Observable for modulo" + modulo + " started") - + if (firstOnly) numbers.take(1) else numbers - }).flatten.toBlockingObservable.foreach(println(_)) + }).flatten.toBlockingObservable.foreach(println(_)) } - + @Test def timingTest1() { val numbersByModulo3 = Observable.interval(1000 millis).take(9).groupBy(_ % 3) - + val t0 = System.currentTimeMillis - + (for ((modulo, numbers) <- numbersByModulo3) yield { println("Observable for modulo" + modulo + " started at t = " + (System.currentTimeMillis - t0)) numbers.take(1) // <- TODO very unexpected //numbers }).flatten.toBlockingObservable.foreach(println(_)) } - + @Test def groupByExample() { val medalsByCountry = Olympics.mountainBikeMedals.groupBy(medal => medal.country) - - val firstMedalOfEachCountry = + + val firstMedalOfEachCountry = for ((country, medals) <- medalsByCountry; firstMedal <- medals.take(1)) yield firstMedal - + firstMedalOfEachCountry.subscribe(medal => { println(s"${medal.country} wins its first medal in ${medal.year}") }) - + waitFor(firstMedalOfEachCountry) } - + @Test def olympicsExample() { val (go, medals) = Olympics.mountainBikeMedals.publish medals.subscribe(println(_)) go() - waitFor(medals) + //waitFor(medals) } - + @Test def exampleWithoutPublish() { val unshared = Observable(1 to 4) unshared.subscribe(n => println(s"subscriber 1 gets $n")) unshared.subscribe(n => println(s"subscriber 2 gets $n")) } - + @Test def exampleWithPublish() { val unshared = Observable(1 to 4) val (startFunc, shared) = unshared.publish @@ -254,11 +254,11 @@ class RxScalaDemo extends JUnitSuite { shared.subscribe(n => println(s"subscriber 2 gets $n")) startFunc() } - + def doLater(waitTime: Duration, action: () => Unit): Unit = { Observable.interval(waitTime).take(1).subscribe(_ => action()) } - + @Test def exampleWithoutReplay() { val numbers = Observable.interval(1000 millis).take(6) val (startFunc, sharedNumbers) = numbers.publish @@ -268,7 +268,7 @@ class RxScalaDemo extends JUnitSuite { doLater(3500 millis, () => { sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) }) waitFor(sharedNumbers) } - + @Test def exampleWithReplay() { val numbers = Observable.interval(1000 millis).take(6) val (startFunc, sharedNumbers) = numbers.replay @@ -278,111 +278,111 @@ class RxScalaDemo extends JUnitSuite { doLater(3500 millis, () => { sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) }) waitFor(sharedNumbers) } - + @Test def testSingleOption() { assertEquals(None, Observable(1, 2).toBlockingObservable.singleOption) assertEquals(Some(1), Observable(1) .toBlockingObservable.singleOption) assertEquals(None, Observable() .toBlockingObservable.singleOption) } - + // We can't put a general average method into Observable.scala, because Scala's Numeric // does not have scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum) def doubleAverage(o: Observable[Double]): Observable[Double] = { for ((finalSum, finalCount) <- o.foldLeft((0.0, 0))({case ((sum, count), elem) => (sum+elem, count+1)})) - yield finalSum / finalCount + yield finalSum / finalCount } - + @Test def averageExample() { println(doubleAverage(Observable()).toBlockingObservable.single) println(doubleAverage(Observable(0)).toBlockingObservable.single) println(doubleAverage(Observable(4.44)).toBlockingObservable.single) println(doubleAverage(Observable(1, 2, 3.5)).toBlockingObservable.single) } - + @Test def testSum() { assertEquals(10, Observable(1, 2, 3, 4).sum.toBlockingObservable.single) assertEquals(6, Observable(4, 2).sum.toBlockingObservable.single) assertEquals(0, Observable[Int]().sum.toBlockingObservable.single) } - + @Test def testProduct() { assertEquals(24, Observable(1, 2, 3, 4).product.toBlockingObservable.single) assertEquals(8, Observable(4, 2).product.toBlockingObservable.single) assertEquals(1, Observable[Int]().product.toBlockingObservable.single) } - + @Test def mapWithIndexExample() { // We don't need mapWithIndex because we already have zipWithIndex, which we can easily // combine with map: Observable("a", "b", "c").zipWithIndex.map(pair => pair._1 + " has index " + pair._2) - .toBlockingObservable.foreach(println(_)) - + .toBlockingObservable.foreach(println(_)) + // Or even nicer with for-comprehension syntax: (for ((letter, index) <- Observable("a", "b", "c").zipWithIndex) yield letter + " has index " + index) - .toBlockingObservable.foreach(println(_)) + .toBlockingObservable.foreach(println(_)) } - + // source Observables are all known: @Test def zip3Example() { val o = Observable.zip(Observable(1, 2), Observable(10, 20), Observable(100, 200)) (for ((n1, n2, n3) <- o) yield s"$n1, $n2 and $n3") - .toBlockingObservable.foreach(println(_)) + .toBlockingObservable.foreach(println(_)) } // source Observables are in an Observable: @Test def zipManyObservableExample() { val observables = Observable(Observable(1, 2), Observable(10, 20), Observable(100, 200)) (for (seq <- Observable.zip(observables)) yield seq.mkString("(", ", ", ")")) - .toBlockingObservable.foreach(println(_)) + .toBlockingObservable.foreach(println(_)) } - + @Test def takeFirstWithCondition() { val condition: Int => Boolean = _ >= 3 assertEquals(3, Observable(1, 2, 3, 4).filter(condition).first.toBlockingObservable.single) } - + @Test def firstOrDefaultWithCondition() { val condition: Int => Boolean = _ >= 3 assertEquals(3, Observable(1, 2, 3, 4).filter(condition).firstOrElse(10).toBlockingObservable.single) assertEquals(10, Observable(-1, 0, 1).filter(condition).firstOrElse(10).toBlockingObservable.single) } - + def square(x: Int): Int = { println(s"$x*$x is being calculated on thread ${Thread.currentThread().getId()}") Thread.sleep(100) // calculating a square is heavy work :) x*x } - + def work(o1: Observable[Int]): Observable[String] = { println(s"map() is being called on thread ${Thread.currentThread().getId()}") o1.map(i => s"The square of $i is ${square(i)}") } - - @Test def parallelExample() { + + @Test def parallelExample() { val t0 = System.currentTimeMillis() Observable(1 to 10).parallel(work(_)).toBlockingObservable.foreach(println(_)) println(s"Work took ${System.currentTimeMillis()-t0} ms") } - + @Test def exampleWithoutParallel() { val t0 = System.currentTimeMillis() work(Observable(1 to 10)).toBlockingObservable.foreach(println(_)) println(s"Work took ${System.currentTimeMillis()-t0} ms") } - + @Test def toSortedList() { assertEquals(Seq(7, 8, 9, 10), Observable(10, 7, 8, 9).toSeq.map(_.sorted).toBlockingObservable.single) val f = (a: Int, b: Int) => b < a assertEquals(Seq(10, 9, 8, 7), Observable(10, 7, 8, 9).toSeq.map(_.sortWith(f)).toBlockingObservable.single) } - + @Test def timestampExample() { val timestamped = Observable.interval(100 millis).take(3).timestamp.toBlockingObservable for ((millis, value) <- timestamped if value > 0) { println(value + " at t = " + millis) } } - + @Test def materializeExample1() { def printObservable[T](o: Observable[T]): Unit = { import Notification._ @@ -392,15 +392,15 @@ class RxScalaDemo extends JUnitSuite { case OnError(err) => println("Error: " + err.getMessage) }) } - + val o1 = Observable.interval(100 millis).take(3) val o2 = Observable(new IOException("Oops")) printObservable(o1) - waitFor(o1) + //waitFor(o1) printObservable(o2) - waitFor(o2) + //waitFor(o2) } - + @Test def materializeExample2() { import Notification._ Observable(1, 2, 3).materialize.subscribe(n => n match { @@ -409,16 +409,16 @@ class RxScalaDemo extends JUnitSuite { case OnError(err) => println("Error: " + err.getMessage) }) } - + @Test def elementAtReplacement() { assertEquals("b", Observable("a", "b", "c").drop(1).first.toBlockingObservable.single) } - + @Test def elementAtOrDefaultReplacement() { assertEquals("b", Observable("a", "b", "c").drop(1).firstOrElse("!").toBlockingObservable.single) assertEquals("!!", Observable("a", "b", "c").drop(10).firstOrElse("!!").toBlockingObservable.single) } - + @Test def observableLikeFuture1() { implicit val scheduler = Schedulers.threadPoolForIO val o1 = observable { @@ -434,22 +434,22 @@ class RxScalaDemo extends JUnitSuite { println((o1 merge o2).first.toBlockingObservable.single) println(System.currentTimeMillis - t1) } - + @Test def observableLikeFuture2() { class Friend {} val session = new Object { def getFriends: List[Friend] = List(new Friend, new Friend) } - + implicit val scheduler = Schedulers.threadPoolForIO val o: Observable[List[Friend]] = observable { - session.getFriends + session.getFriends } o.subscribe( friendList => println(friendList), err => println(err.getMessage) ) - + Thread.sleep(1500) // or convert to BlockingObservable } @@ -459,10 +459,10 @@ class RxScalaDemo extends JUnitSuite { } def output(s: String): Unit = println(s) - + // blocks until obs has completed def waitFor[T](obs: Observable[T]): Unit = { obs.toBlockingObservable.toIterable.last } - -} + +} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala b/language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala new file mode 100644 index 0000000000..9be18085b9 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala @@ -0,0 +1,114 @@ +import org.junit.{Assert, Test} +import org.scalatest.junit.JUnitSuite +import rx.lang.scala.subscriptions.{MultipleAssignmentSubscription, CompositeSubscription, BooleanSubscription, Subscription} + +class SubscriptionTests extends JUnitSuite { + + @Test + def anonymousSubscriptionCreate() { + val subscription = Subscription{} + Assert.assertNotNull(subscription) + } + + @Test + def anonymousSubscriptionDispose() { + var unsubscribed = false + val subscription = Subscription{ unsubscribed = true } + Assert.assertFalse(unsubscribed) + subscription.unsubscribe() + Assert.assertTrue(unsubscribed) + } + + @Test + def emptySubscription() { + val subscription = Subscription() + subscription.unsubscribe() + } + + @Test + def booleanSubscription() { + val subscription = BooleanSubscription() + Assert.assertFalse(subscription.isUnsubscribed) + subscription.unsubscribe() + Assert.assertTrue(subscription.isUnsubscribed) + subscription.unsubscribe() + Assert.assertTrue(subscription.isUnsubscribed) + } + + @Test + def compositeSubscriptionAdd() { + + var u0 = false + val s0 = BooleanSubscription{ u0 = true } + + var u1 = false + val s1 = Subscription{ u1 = true } + + val composite = CompositeSubscription() + + Assert.assertFalse(composite.isUnsubscribed) + + composite += s0 + composite += s1 + + composite.unsubscribe() + + Assert.assertTrue(composite.isUnsubscribed) + Assert.assertTrue(s0.isUnsubscribed) + Assert.assertTrue(u0) + Assert.assertTrue(u1) + + val s2 = BooleanSubscription() + Assert.assertFalse(s2.isUnsubscribed) + composite += s2 + Assert.assertTrue(s2.isUnsubscribed) + + } + + @Test + def compositeSubscriptionRemove() { + + val s0 = BooleanSubscription() + val composite = CompositeSubscription() + + composite += s0 + Assert.assertFalse(s0.isUnsubscribed) + composite -= s0 + Assert.assertTrue(s0.isUnsubscribed) + + composite.unsubscribe() + + Assert.assertTrue(composite.isUnsubscribed) + } + + @Test + def multiAssignmentSubscriptionAdd() { + + val s0 = BooleanSubscription() + val s1 = BooleanSubscription() + val multiple = MultipleAssignmentSubscription() + + Assert.assertFalse(multiple.isUnsubscribed) + + multiple.subscription = s0 + Assert.assertEquals(s0.asJavaSubscription, multiple.subscription.asJavaSubscription) + + multiple.subscription = s1 + Assert.assertEquals(s1.asJavaSubscription, multiple.subscription.asJavaSubscription) + + Assert.assertFalse(s0.isUnsubscribed) + Assert.assertFalse(s1.isUnsubscribed) + + multiple.unsubscribe() + + Assert.assertTrue(multiple.isUnsubscribed) + Assert.assertFalse(s0.isUnsubscribed) + Assert.assertTrue(s1.isUnsubscribed) + + val s2 = BooleanSubscription() + Assert.assertFalse(s2.isUnsubscribed) + multiple.subscription = s2 + Assert.assertTrue(s2.isUnsubscribed) + } + +} diff --git a/language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala b/language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala new file mode 100644 index 0000000000..006909a5dd --- /dev/null +++ b/language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala @@ -0,0 +1,87 @@ +import org.junit.{Ignore, Assert, Test} +import org.scalatest.junit.JUnitSuite +import rx.lang.scala.Observable +import scala.Predef.String + +class UnitTestSuite extends JUnitSuite { + + // Tests which needn't be run: + +@Ignore +def testCovariance = { + //println("hey, you shouldn't run this test") + + val o1: Observable[Nothing] = Observable() + val o2: Observable[Int] = o1 + val o3: Observable[App] = o1 + val o4: Observable[Any] = o2 + val o5: Observable[Any] = o3 +} + +// Tests which have to be run: + +@Test + def testDematerialize() { + val o = Observable(1, 2, 3) + val mat = o.materialize + val demat = mat.dematerialize + + // correctly rejected: + //val wrongDemat = Observable("hello").dematerialize + + Assert.assertEquals(demat.toBlockingObservable.toIterable.toList, List(1, 2, 3)) +} + +// Test that Java's firstOrDefault propagates errors. +// If this changes (i.e. it suppresses errors and returns default) then Scala's firstOrElse +// should be changed accordingly. +@Test def testJavaFirstOrDefault() { + Assert.assertEquals(1, rx.Observable.from(1, 2).firstOrDefault(10).toBlockingObservable().single) + Assert.assertEquals(10, rx.Observable.empty().firstOrDefault(10).toBlockingObservable().single) + val msg = "msg6251" + var receivedMsg = "none" + try { + rx.Observable.error(new Exception(msg)).firstOrDefault(10).toBlockingObservable().single + } catch { + case e: Exception => receivedMsg = e.getCause().getMessage() + } + Assert.assertEquals(receivedMsg, msg) +} + +@Test def testFirstOrElse() { + def mustNotBeCalled: String = sys.error("this method should not be called") + def mustBeCalled: String = "this is the default value" + Assert.assertEquals("hello", Observable("hello").firstOrElse(mustNotBeCalled).toBlockingObservable.single) + Assert.assertEquals("this is the default value", Observable().firstOrElse(mustBeCalled).toBlockingObservable.single) +} + +@Test def testFirstOrElseWithError() { + val msg = "msg6251" + var receivedMsg = "none" + try { + Observable[Int](new Exception(msg)).firstOrElse(10).toBlockingObservable.single + } catch { + case e: Exception => receivedMsg = e.getCause().getMessage() + } + Assert.assertEquals(receivedMsg, msg) +} + + /* + @Test def testHead() { + val observer = mock(classOf[Observer[Int]]) + val o = Observable().head + val sub = o.subscribe(observer) + + verify(observer, never).onNext(any(classOf[Int])) + verify(observer, never).onCompleted() + verify(observer, times(1)).onError(any(classOf[NoSuchElementException])) + } + */ + + @Test def testTest() = { + val a: Observable[Int] = Observable() + Assert.assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.toIterable.last) + //println("This UnitTestSuite.testTest() for rx.lang.scala.Observable") + } + +} diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala deleted file mode 100644 index c5c13d3070..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ /dev/null @@ -1,369 +0,0 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.scala - -import java.util.Calendar - -import scala.collection.SortedMap -import scala.reflect.runtime.universe -import scala.reflect.runtime.universe.Symbol -import scala.reflect.runtime.universe.Type -import scala.reflect.runtime.universe.typeOf - -import org.junit.Ignore -import org.junit.Test -import org.scalatest.junit.JUnitSuite - -/** - * These tests can be used to check if all methods of the Java Observable have a corresponding - * method in the Scala Observable. - * - * These tests don't contain any assertions, so they will always succeed, but they print their - * results to stdout. - */ -class CompletenessTest extends JUnitSuite { - - // some frequently used comments: - val unnecessary = "[considered unnecessary in Scala land]" - val deprecated = "[deprecated in RxJava]" - val averageProblem = "[We can't have a general average method because Scala's `Numeric` does not have " + - "scalar multiplication (we would need to calculate `(1.0/numberOfElements)*sum`). " + - "You can use `fold` instead to accumulate `sum` and `numberOfElements` and divide at the end.]" - val commentForFirstWithPredicate = "[use `.filter(condition).first`]" - val fromFuture = "[TODO: Decide how Scala Futures should relate to Observables. Should there be a " + - "common base interface for Future and Observable? And should Futures also have an unsubscribe method?]" - - /** - * Maps each method from the Java Observable to its corresponding method in the Scala Observable - */ - val correspondence = defaultMethodCorrespondence ++ correspondenceChanges // ++ overrides LHS with RHS - - /** - * Creates default method correspondence mappings, assuming that Scala methods have the same - * name and the same argument types as in Java - */ - def defaultMethodCorrespondence: Map[String, String] = { - val allMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.Observable[_]]) - val tuples = for (javaM <- allMethods) yield (javaM, javaMethodSignatureToScala(javaM)) - tuples.toMap - } - - /** - * Manually added mappings from Java Observable methods to Scala Observable methods - */ - def correspondenceChanges = Map( - // manually added entries for Java instance methods - "aggregate(Func2[T, T, T])" -> "reduce((U, U) => U)", - "aggregate(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", - "all(Func1[_ >: T, Boolean])" -> "forall(T => Boolean)", - "buffer(Long, Long, TimeUnit)" -> "buffer(Duration, Duration)", - "buffer(Long, Long, TimeUnit, Scheduler)" -> "buffer(Duration, Duration, Scheduler)", - "count()" -> "length", - "dematerialize()" -> "dematerialize(<:<[Observable[T], Observable[Notification[U]]])", - "elementAt(Int)" -> "[use `.drop(index).first`]", - "elementAtOrDefault(Int, T)" -> "[use `.drop(index).firstOrElse(default)`]", - "first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, - "firstOrDefault(T)" -> "firstOrElse(=> U)", - "firstOrDefault(Func1[_ >: T, Boolean], T)" -> "[use `.filter(condition).firstOrElse(default)`]", - "groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "[use `groupBy` and `map`]", - "mapMany(Func1[_ >: T, _ <: Observable[_ <: R]])" -> "flatMap(T => Observable[R])", - "mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> "[combine `zipWithIndex` with `map` or with a for comprehension]", - "onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> "onErrorResumeNext(Throwable => Observable[U])", - "onErrorResumeNext(Observable[_ <: T])" -> "onErrorResumeNext(Observable[U])", - "onErrorReturn(Func1[Throwable, _ <: T])" -> "onErrorReturn(Throwable => U)", - "onExceptionResumeNext(Observable[_ <: T])" -> "onExceptionResumeNext(Observable[U])", - "parallel(Func1[Observable[T], Observable[R]])" -> "parallel(Observable[T] => Observable[R])", - "parallel(Func1[Observable[T], Observable[R]], Scheduler)" -> "parallel(Observable[T] => Observable[R], Scheduler)", - "reduce(Func2[T, T, T])" -> "reduce((U, U) => U)", - "reduce(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", - "scan(Func2[T, T, T])" -> unnecessary, - "scan(R, Func2[R, _ >: T, R])" -> "scan(R)((R, T) => R)", - "skip(Int)" -> "drop(Int)", - "skipWhile(Func1[_ >: T, Boolean])" -> "dropWhile(T => Boolean)", - "skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary, - "startWith(Iterable[T])" -> "[unnecessary because we can just use `++` instead]", - "takeFirst()" -> "first", - "takeFirst(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, - "takeLast(Int)" -> "takeRight(Int)", - "takeWhileWithIndex(Func2[_ >: T, _ >: Integer, Boolean])" -> "[use `.zipWithIndex.takeWhile{case (elem, index) => condition}.map(_._1)`]", - "toList()" -> "toSeq", - "toSortedList()" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)`]", - "toSortedList(Func2[_ >: T, _ >: T, Integer])" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))`]", - "where(Func1[_ >: T, Boolean])" -> "filter(T => Boolean)", - "window(Long, Long, TimeUnit)" -> "window(Duration, Duration)", - "window(Long, Long, TimeUnit, Scheduler)" -> "window(Duration, Duration, Scheduler)", - - // manually added entries for Java static methods - "average(Observable[Integer])" -> averageProblem, - "averageDoubles(Observable[Double])" -> averageProblem, - "averageFloats(Observable[Float])" -> averageProblem, - "averageLongs(Observable[Long])" -> averageProblem, - "create(OnSubscribeFunc[T])" -> "apply(Observer[T] => Subscription)", - "combineLatest(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "combineLatest(Observable[U])", - "concat(Observable[_ <: Observable[_ <: T]])" -> "concat(<:<[Observable[T], Observable[Observable[U]]])", - "defer(Func0[_ <: Observable[_ <: T]])" -> "defer(=> Observable[T])", - "empty()" -> "apply(T*)", - "error(Throwable)" -> "apply(Throwable)", - "from(Array[T])" -> "apply(T*)", - "from(Iterable[_ <: T])" -> "apply(T*)", - "from(Future[_ <: T])" -> fromFuture, - "from(Future[_ <: T], Long, TimeUnit)" -> fromFuture, - "from(Future[_ <: T], Scheduler)" -> fromFuture, - "just(T)" -> "apply(T*)", - "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[U])", - "merge(Observable[_ <: Observable[_ <: T]])" -> "flatten(<:<[Observable[T], Observable[Observable[U]]])", - "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[U])", - "mergeDelayError(Observable[_ <: Observable[_ <: T]])" -> "flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])", - "range(Int, Int)" -> "apply(Range)", - "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "[use `(first zip second) map (p => p._1 == p._2)`]", - "sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "[use `(first zip second) map (p => equality(p._1, p._2))`]", - "sum(Observable[Integer])" -> "sum(Numeric[U])", - "sumDoubles(Observable[Double])" -> "sum(Numeric[U])", - "sumFloats(Observable[Float])" -> "sum(Numeric[U])", - "sumLongs(Observable[Long])" -> "sum(Numeric[U])", - "synchronize(Observable[T])" -> "synchronize", - "switchDo(Observable[_ <: Observable[_ <: T]])" -> deprecated, - "switchOnNext(Observable[_ <: Observable[_ <: T]])" -> "switch(<:<[Observable[T], Observable[Observable[U]]])", - "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method `zip` and `map`]", - "zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]", - "zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]" - ) ++ List.iterate("T", 9)(s => s + ", T").map( - // all 9 overloads of startWith: - "startWith(" + _ + ")" -> "[unnecessary because we can just use `++` instead]" - ).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( - // concat 2-9 - "concat(" + _ + ")" -> "[unnecessary because we can use `++` instead or `Observable(o1, o2, ...).concat`]" - ).drop(1).toMap ++ List.iterate("T", 10)(s => s + ", T").map( - // all 10 overloads of from: - "from(" + _ + ")" -> "apply(T*)" - ).toMap ++ (3 to 9).map(i => { - // zip3-9: - val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") - val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") - ("zip(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", unnecessary) - }).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( - // merge 3-9: - "merge(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flatten` instead]" - ).drop(2).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( - // mergeDelayError 3-9: - "mergeDelayError(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flattenDelayError` instead]" - ).drop(2).toMap ++ (3 to 9).map(i => { - // combineLatest 3-9: - val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") - val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") - ("combineLatest(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", "[If C# doesn't need it, Scala doesn't need it either ;-)]") - }).toMap - - def removePackage(s: String) = s.replaceAll("(\\w+\\.)+(\\w+)", "$2") - - def methodMembersToMethodStrings(members: Iterable[Symbol]): Iterable[String] = { - for (member <- members; alt <- member.asTerm.alternatives) yield { - val m = alt.asMethod - // multiple parameter lists in case of curried functions - val paramListStrs = for (paramList <- m.paramss) yield { - paramList.map( - symb => removePackage(symb.typeSignature.toString.replaceAll(",(\\S)", ", $1")) - ).mkString("(", ", ", ")") - } - val name = alt.asMethod.name.decoded - name + paramListStrs.mkString("") - } - } - - def getPublicInstanceMethods(tp: Type): Iterable[String] = { - // declarations: => only those declared in Observable - // members => also those of superclasses - methodMembersToMethodStrings(tp.declarations.filter(m => m.isMethod && m.isPublic)) - // TODO how can we filter out instance methods which were put into companion because - // of extends AnyVal in a way which does not depend on implementation-chosen name '$extension'? - .filter(! _.contains("$extension")) - } - - // also applicable for Java types - def getPublicInstanceAndCompanionMethods(tp: Type): Iterable[String] = - getPublicInstanceMethods(tp) ++ - getPublicInstanceMethods(tp.typeSymbol.companionSymbol.typeSignature) - - def printMethodSet(title: String, tp: Type) { - println("\n" + title) - println(title.map(_ => '-') + "\n") - getPublicInstanceMethods(tp).toList.sorted.foreach(println(_)) - } - - @Ignore // because spams output - @Test def printJavaInstanceMethods: Unit = { - printMethodSet("Instance methods of rx.Observable", - typeOf[rx.Observable[_]]) - } - - @Ignore // because spams output - @Test def printScalaInstanceMethods: Unit = { - printMethodSet("Instance methods of rx.lang.scala.Observable", - typeOf[rx.lang.scala.Observable[_]]) - } - - @Ignore // because spams output - @Test def printJavaStaticMethods: Unit = { - printMethodSet("Static methods of rx.Observable", - typeOf[rx.Observable[_]].typeSymbol.companionSymbol.typeSignature) - } - - @Ignore // because spams output - @Test def printScalaCompanionMethods: Unit = { - printMethodSet("Companion methods of rx.lang.scala.Observable", - typeOf[rx.lang.scala.Observable.type]) - } - - def javaMethodSignatureToScala(s: String): String = { - s.replaceAllLiterally("Long, TimeUnit", "Duration") - .replaceAll("Action0", "() => Unit") - // nested [] can't be parsed with regex, so these will have to be added manually - .replaceAll("Action1\\[([^]]*)\\]", "$1 => Unit") - .replaceAll("Action2\\[([^]]*), ([^]]*)\\]", "($1, $2) => Unit") - .replaceAll("Func0\\[([^]]*)\\]", "() => $1") - .replaceAll("Func1\\[([^]]*), ([^]]*)\\]", "$1 => $2") - .replaceAll("Func2\\[([^]]*), ([^]]*), ([^]]*)\\]", "($1, $2) => $3") - .replaceAllLiterally("_ <: ", "") - .replaceAllLiterally("_ >: ", "") - .replaceAll("(\\w+)\\(\\)", "$1") - } - - @Ignore // because spams output - @Test def printDefaultMethodCorrespondence: Unit = { - println("\nDefault Method Correspondence") - println( "-----------------------------\n") - val c = SortedMap(defaultMethodCorrespondence.toSeq : _*) - val len = c.keys.map(_.length).max + 2 - for ((javaM, scalaM) <- c) { - println(s""" %-${len}s -> %s,""".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) - } - } - - @Ignore // because spams output - @Test def printCorrectedMethodCorrespondence: Unit = { - println("\nCorrected Method Correspondence") - println( "-------------------------------\n") - val c = SortedMap(correspondence.toSeq : _*) - for ((javaM, scalaM) <- c) { - println("%s -> %s,".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) - } - } - - def checkMethodPresence(expectedMethods: Iterable[String], tp: Type): Unit = { - val actualMethods = getPublicInstanceAndCompanionMethods(tp).toSet - val expMethodsSorted = expectedMethods.toList.sorted - var good = 0 - var bad = 0 - for (m <- expMethodsSorted) if (actualMethods.contains(m) || m.charAt(0) == '[') { - good += 1 - } else { - bad += 1 - println(s"Warning: $m is NOT present in $tp") - } - val status = if (bad == 0) "SUCCESS" else "BAD" - println(s"$status: $bad out of ${bad+good} methods were not found in $tp") - } - - @Test def checkScalaMethodPresenceVerbose: Unit = { - println("\nTesting that all mentioned Scala methods exist") - println( "----------------------------------------------\n") - - val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet - var good = 0 - var bad = 0 - for ((javaM, scalaM) <- SortedMap(correspondence.toSeq :_*)) { - if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') { - good += 1 - } else { - bad += 1 - println(s"Warning:") - println(s"$scalaM is NOT present in Scala Observable") - println(s"$javaM is the method in Java Observable generating this warning") - } - } - val status = if (bad == 0) "SUCCESS" else "BAD" - println(s"\n$status: $bad out of ${bad+good} methods were not found in Scala Observable") - } - - def setTodoForMissingMethods(corresp: Map[String, String]): Map[String, String] = { - val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet - for ((javaM, scalaM) <- corresp) yield - (javaM, if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') scalaM else "[**TODO: missing**]") - } - - @Test def checkJavaMethodPresence: Unit = { - println("\nTesting that all mentioned Java methods exist") - println( "---------------------------------------------\n") - checkMethodPresence(correspondence.keys, typeOf[rx.Observable[_]]) - } - - @Ignore // because we prefer the verbose version - @Test def checkScalaMethodPresence: Unit = { - checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable[_]]) - } - - def scalaToJavaSignature(s: String) = - s.replaceAllLiterally("_ <:", "? extends") - .replaceAllLiterally("_ >:", "? super") - .replaceAllLiterally("[", "<") - .replaceAllLiterally("]", ">") - .replaceAllLiterally("Array", "T[]") - - def escapeJava(s: String) = - s.replaceAllLiterally("<", "<") - .replaceAllLiterally(">", ">") - - @Ignore // because spams output - @Test def printMarkdownCorrespondenceTable() { - def isInteresting(p: (String, String)): Boolean = - p._1.replaceAllLiterally("()", "") != p._2 - def groupingKey(p: (String, String)): (String, String) = - (if (p._1.startsWith("average")) "average" else p._1.takeWhile(_ != '('), p._2) - def formatJavaCol(name: String, alternatives: Iterable[String]): String = { - alternatives.toList.sorted.map(scalaToJavaSignature(_)).map(s => { - if (s.length > 64) { - val toolTip = escapeJava(s) - "" + name + "(...)" - } else { - "`" + s + "`" - } - }).mkString("
") - } - def formatScalaCol(s: String): String = - if (s.startsWith("[") && s.endsWith("]")) s.drop(1).dropRight(1) else "`" + s + "`" - def escape(s: String) = s.replaceAllLiterally("[", "<").replaceAllLiterally("]", ">") - - println(""" -## Comparison of Scala Observable and Java Observable - -Note: -* This table contains both static methods and instance methods. -* If a signature is too long, move your mouse over it to get the full signature. - - -| Java Method | Scala Method | -|-------------|--------------|""") - - val ps = setTodoForMissingMethods(correspondence) - - (for (((javaName, scalaCol), pairs) <- ps.groupBy(groupingKey(_)).toList.sortBy(_._1._1)) yield { - "| " + formatJavaCol(javaName, pairs.map(_._1)) + " | " + formatScalaCol(scalaCol) + " |" - }).foreach(println(_)) - println(s"\nThis table was generated on ${Calendar.getInstance().getTime()}.") - println(s"**Do not edit**. Instead, edit `${getClass().getCanonicalName()}`.") - } - -} From 5c467b30cc65c090cde592fd971116271de0fe2b Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 19 Nov 2013 17:12:44 -0800 Subject: [PATCH 2/3] Reorg Scala Structure - make Eclipse and Java/Scala interop happy --- .../rx/lang/scala/examples/MovieLibUsage.java | 22 +++++++++++-------- .../lang/scala/examples}/MovieLib.scala | 0 .../lang/scala/examples}/Olympics.scala | 0 .../scala}/ImplicitFunctionConversions.scala | 4 ++++ .../{ => rx/lang/scala}/Notification.scala | 0 .../{ => rx/lang/scala}/Observable.scala | 4 ++++ .../scala/{ => rx/lang/scala}/Observer.scala | 0 .../scala/{ => rx/lang/scala}/Scheduler.scala | 4 ++-- .../{ => rx/lang/scala}/WithFilter.scala | 5 +++++ .../lang/scala}/concurrency/Schedulers.scala | 0 .../scala}/concurrency/TestScheduler.scala | 0 .../observables/BlockingObservable.scala | 0 .../lang/scala}/subjects/AsyncSubject.scala | 0 .../scala}/subjects/BehaviorSubject.scala | 0 .../lang/scala}/subjects/PublishSubject.scala | 0 .../lang/scala}/subjects/ReplaySubject.scala | 0 .../lang/scala}/subjects/Subject.scala | 2 ++ .../subscriptions/BooleanSubscription.scala | 0 .../subscriptions/CompositeSubscription.scala | 0 .../MultiAssignmentSubscription.scala | 0 .../subscriptions/SerialSubscription.scala | 0 .../scala}/subscriptions/Subscription.scala | 0 .../lang/scala}/subscriptions/scala.scala | 1 + .../scala/{ => rx/lang/scala}/util/util.scala | 0 .../lang/scala/examples}/RxJavaDemos.scala | 7 ++++-- .../scala/examples}/SubscriptionTests.scala | 2 ++ .../lang/scala/examples}/UnitTestSuite.scala | 3 ++- 27 files changed, 40 insertions(+), 14 deletions(-) rename language-adaptors/rxjava-scala/src/examples/scala/{ => rx/lang/scala/examples}/MovieLib.scala (100%) rename language-adaptors/rxjava-scala/src/examples/scala/{ => rx/lang/scala/examples}/Olympics.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/ImplicitFunctionConversions.scala (97%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/Notification.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/Observable.scala (99%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/Observer.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/Scheduler.scala (98%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/WithFilter.scala (81%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/concurrency/Schedulers.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/concurrency/TestScheduler.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/observables/BlockingObservable.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subjects/AsyncSubject.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subjects/BehaviorSubject.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subjects/PublishSubject.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subjects/ReplaySubject.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subjects/Subject.scala (91%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/BooleanSubscription.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/CompositeSubscription.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/MultiAssignmentSubscription.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/SerialSubscription.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/Subscription.scala (100%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/subscriptions/scala.scala (94%) rename language-adaptors/rxjava-scala/src/main/scala/{ => rx/lang/scala}/util/util.scala (100%) rename language-adaptors/rxjava-scala/src/test/scala/{ => rx/lang/scala/examples}/RxJavaDemos.scala (98%) rename language-adaptors/rxjava-scala/src/test/scala/{ => rx/lang/scala/examples}/SubscriptionTests.scala (98%) rename language-adaptors/rxjava-scala/src/test/scala/{ => rx/lang/scala/examples}/UnitTestSuite.scala (98%) diff --git a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java index 19a618d158..fde62e0dd8 100644 --- a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java +++ b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java @@ -1,12 +1,12 @@ /** * Copyright 2013 Netflix, Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,7 +18,7 @@ import rx.Observable; import rx.lang.scala.examples.Movie; import rx.lang.scala.examples.MovieLib; - +import rx.util.functions.Action1; import static rx.lang.scala.ImplicitFunctionConversions.toScalaObservable; public class MovieLibUsage { @@ -29,13 +29,17 @@ public static void main(String[] args) { new Movie(3000), new Movie(1000), new Movie(2000) - ); + ); MovieLib lib = new MovieLib(toScalaObservable(movies)); - lib.longMovies().asJavaObservable().subscribe(m -> - System.out.println("A movie of length " + m.lengthInSeconds() + "s") - ); + lib.longMovies().asJavaObservable().subscribe(new Action1() { + + @Override + public void call(Movie m) { + System.out.println("A movie of length " + m.lengthInSeconds() + "s"); + } + }); } } diff --git a/language-adaptors/rxjava-scala/src/examples/scala/MovieLib.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/examples/scala/MovieLib.scala rename to language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala diff --git a/language-adaptors/rxjava-scala/src/examples/scala/Olympics.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/examples/scala/Olympics.scala rename to language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala similarity index 97% rename from language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala index 67f24af638..458f5fedbc 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/ImplicitFunctionConversions.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala @@ -21,6 +21,10 @@ import rx.lang.scala._ import rx.util.functions._ import scala.collection.Seq import rx.lang.scala.subscriptions.Subscription +import java.{lang => jlang} +import scala.language.implicitConversions +import rx.lang.scala.Observer +import rx.lang.scala.Scheduler /** * These function conversions convert between Scala functions and Rx `Func`s and `Action`s. diff --git a/language-adaptors/rxjava-scala/src/main/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/Notification.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala similarity index 99% rename from language-adaptors/rxjava-scala/src/main/scala/Observable.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index a0dfaf3601..0d8cbb7166 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -19,6 +19,10 @@ package rx.lang.scala import rx.util.functions.FuncN import rx.Observable.OnSubscribeFunc +import rx.lang.scala.Notification +import rx.lang.scala.ImplicitFunctionConversions +import rx.lang.scala.Observer +import rx.lang.scala.Scheduler /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/Observer.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/Observer.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala similarity index 98% rename from language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala index 10e5bc0d15..8060dbaa36 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/Scheduler.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala @@ -2,14 +2,14 @@ package rx.lang.scala import java.util.Date import scala.concurrent.duration.Duration -import scala.language.postfixOps import ImplicitFunctionConversions.scalaFunction0ProducingUnitToAction0 import ImplicitFunctionConversions.schedulerActionToFunc2 import rx.util.functions.{Action0, Action1, Func2} import rx.lang.scala.subscriptions.Subscription /** - * Represents an object that schedules units of work. + * Represents an object thatimport rx.lang.scala.ImplicitFunctionConversions + schedules units of work. */ trait Scheduler { def asJavaScheduler: rx.Scheduler diff --git a/language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala similarity index 81% rename from language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala index 9b36d3ca74..61b25b8784 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/WithFilter.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala @@ -1,5 +1,10 @@ package rx.lang.scala +import rx.lang.scala.ImplicitFunctionConversions + +import ImplicitFunctionConversions.scalaBooleanFunction1ToRxBooleanFunc1 +import ImplicitFunctionConversions.scalaFunction1ToRxFunc1 + // Cannot yet have inner class because of this error message: // "implementation restriction: nested class is not allowed in value class. // This restriction is planned to be removed in subsequent releases." diff --git a/language-adaptors/rxjava-scala/src/main/scala/concurrency/Schedulers.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/concurrency/Schedulers.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/concurrency/TestScheduler.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/observables/BlockingObservable.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subjects/AsyncSubject.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subjects/BehaviorSubject.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subjects/PublishSubject.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subjects/ReplaySubject.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala similarity index 91% rename from language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala index cb92df90d9..5631b1bdea 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/subjects/Subject.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala @@ -1,5 +1,7 @@ package rx.lang.scala +import rx.lang.scala.Observer + /** * A Subject is an Observable and an Observer at the same time. */ diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/BooleanSubscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/CompositeSubscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/MultiAssignmentSubscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/SerialSubscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/Subscription.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/Subscription.scala diff --git a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/scala.scala similarity index 94% rename from language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/scala.scala index b856d97fd0..d0c7fa1761 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/subscriptions/scala.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/scala.scala @@ -1,5 +1,6 @@ package rx.lang +import rx.lang.scala.Scheduler package object scala { /** diff --git a/language-adaptors/rxjava-scala/src/main/scala/util/util.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/util.scala similarity index 100% rename from language-adaptors/rxjava-scala/src/main/scala/util/util.scala rename to language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/util.scala diff --git a/language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala similarity index 98% rename from language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala rename to language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala index 2580d7297d..130c9f2aa8 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/RxJavaDemos.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/RxJavaDemos.scala @@ -16,13 +16,16 @@ package rx.lang.scala.examples import org.scalatest.junit.JUnitSuite -import scala.language.postfixOps import rx.lang.scala._ import scala.concurrent.duration._ -import org.junit.{Before, Test, Ignore} +import org.junit.Test import org.junit.Assert._ import rx.lang.scala.concurrency.Schedulers import java.io.IOException +import rx.lang.scala.examples.Olympics +import rx.lang.scala.Notification.OnCompleted +import rx.lang.scala.Notification.OnError +import rx.lang.scala.Notification.OnNext //@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily class RxScalaDemo extends JUnitSuite { diff --git a/language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/SubscriptionTests.scala similarity index 98% rename from language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala rename to language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/SubscriptionTests.scala index 9be18085b9..aad6f22f95 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/SubscriptionTests.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/SubscriptionTests.scala @@ -1,3 +1,5 @@ +package rx.lang.scala.examples + import org.junit.{Assert, Test} import org.scalatest.junit.JUnitSuite import rx.lang.scala.subscriptions.{MultipleAssignmentSubscription, CompositeSubscription, BooleanSubscription, Subscription} diff --git a/language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/UnitTestSuite.scala similarity index 98% rename from language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala rename to language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/UnitTestSuite.scala index 006909a5dd..0e93aeb69e 100644 --- a/language-adaptors/rxjava-scala/src/test/scala/UnitTestSuite.scala +++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/examples/UnitTestSuite.scala @@ -1,7 +1,8 @@ +package rx.lang.scala.examples + import org.junit.{Ignore, Assert, Test} import org.scalatest.junit.JUnitSuite import rx.lang.scala.Observable -import scala.Predef.String class UnitTestSuite extends JUnitSuite { From 7fd5183b45a70e9e2ae1894b0b3b24bf180519a3 Mon Sep 17 00:00:00 2001 From: headinthebox Date: Tue, 19 Nov 2013 17:17:28 -0800 Subject: [PATCH 3/3] Updated README --- language-adaptors/rxjava-scala/README.md | 213 ++++++++++++++--------- 1 file changed, 128 insertions(+), 85 deletions(-) diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/README.md index d198abcdb8..5afb2392cc 100644 --- a/language-adaptors/rxjava-scala/README.md +++ b/language-adaptors/rxjava-scala/README.md @@ -1,101 +1,144 @@ -# Scala Adaptor for RxJava - -This adaptor allows to use RxJava in Scala with anonymous functions, e.g. +Alternative Rx bindings for Scala +================================= +The current RxScala binding attempt to optimize for seamless interop between Scala and Java. +The intended interop is illustrated by the following example where in Scala a class is defined that takes +an `Observable[Movie]` that is transformed using RxScala operators: ```scala -val o = Observable.interval(200 millis).take(5) -o.subscribe(n => println("n = " + n)) -Observable(1, 2, 3, 4).reduce(_ + _) +class MovieLib(val moviesStream: Observable[Movie]) { + val threshold = 1200 + def shortMovies: Observable[Movie] = ??? + def longMovies: Observable[Movie] = ??? +} ``` +which is then called in Java, passing a Java `Observable` to the constructor +```java +public void test() { + MovieLib lib = new MovieLib(Observable.from(...)); -For-comprehensions are also supported: - -```scala -val first = Observable(10, 11, 12) -val second = Observable(10, 11, 12) -val booleans = for ((n1, n2) <- (first zip second)) yield (n1 == n2) + lib.longMovies().subscribe(moviePrinter); +} ``` - -Further, this adaptor attempts to expose an API which is as Scala-idiomatic as possible. This means that certain methods have been renamed, their signature was changed, or static methods were changed to instance methods. Some examples: - +The technique used to obtain this transparency is to use a value class with a private constructor that implements +the Rx operators in an idiomatic Scala way, and a companion object that is used to construct instances in Scala ```scala - // instead of concat: -def ++[U >: T](that: Observable[U]): Observable[U] - -// instance method instead of static: -def zip[U](that: Observable[U]): Observable[(T, U)] - -// the implicit evidence argument ensures that dematerialize can only be called on Observables of Notifications: -def dematerialize[U](implicit evidence: T <:< Notification[U]): Observable[U] - -// additional type parameter U with lower bound to get covariance right: -def onErrorResumeNext[U >: T](resumeFunction: Throwable => Observable[U]): Observable[U] +object Observable { + def apply[T](asJava: rx.Observable[_ <: T]): Observable[T] = { new Observable[T](asJava) } +} -// curried in Scala collections, so curry fold also here: -def fold[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] - -// using Duration instead of (long timepan, TimeUnit duration): -def sample(duration: Duration): Observable[T] - -// called skip in Java, but drop in Scala -def drop(n: Int): Observable[T] - -// there's only mapWithIndex in Java, because Java doesn't have tuples: -def zipWithIndex: Observable[(T, Int)] - -// corresponds to Java's toList: -def toSeq: Observable[Seq[T]] - -// the implicit evidence argument ensures that switch can only be called on Observables of Observables: -def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] - -// Java's from becomes apply, and we use Scala Range -def apply(range: Range): Observable[Int] - -// use Bottom type: -def never: Observable[Nothing] +class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) extends AnyVal { + // Idiomatic Scala friendly definitions of Rx operators +} ``` - -Also, the Scala Observable is fully covariant in its type parameter, whereas the Java Observable only achieves partial covariance due to limitations of Java's type system (or if you can fix this, your suggestions are very welcome). - -For more examples, see [RxScalaDemo.scala](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala). - -Scala code using Rx should only import members from `rx.lang.scala` and below. - - -## Documentation - -The API documentation can be found [here](http://rxscala.github.io/scaladoc/index.html#rx.lang.scala.Observable). - -You can build the API documentation yourself by running `./gradlew scaladoc` in the RxJava root directory. - -Then navigate to `RxJava/language-adaptors/rxjava-scala/build/docs/scaladoc/index.html` to display it. - - -## Binaries - -Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22rxjava-scala%22). - -Example for Maven: - -```xml - - com.netflix.rxjava - rxjava-scala - x.y.z - +Since `rx.lang.scala.Observable[T] extends AnyVal`, the underlying representation of `rx.lang.scala.Observable[T]` +is the same as `rx.Observable`. Because `rx.lang.scala.Observable[T]` is an opaque type in Scala, +the Scala programmer only sees the Scala-friendly operators. + +However, in the current the illusion of interop is quickly lost when going beyond this simple example. +For example but type `Notification[T]` and `Scheduler[T]` are defined using wrappers, +and hence they are not compatible with `Notification` respectively `Scheduler`. +For instance, when materializing an `Observable[T]` in Scala to an `Observable[Notification[T]]`, +we lost the seamless interop with `Observable>` on the Java side. + +However, the real problems with seamless interop show up when we try to creating bindings for other Rx types. +In particular types that have inheritance or more structure. + +For example, RxScala currently defines a type synonym `type Observer[-T] = rx.Observer[_ >: T]`, +but no further bindings for observers. +Similarly, for subjects RxScala defines `type Subject[-T, +R] = rx.subjects.Subject[_ >: T, _ <: R]`. +The problem with these definitions is that on the Java side, subjects are defined as: +```scala +public abstract class Subject extends Observable implements Observer { …} ``` +without binding any of the Rx subjects. + +The consequence is that `Subject[S,T]` in Scala is unrelated to `rx.lang.scala.Observable[T]` in Scala, +but shows up as a `rx.Observable[T]`. The problem however is that if we want to expose subjects in Scala +such that they derive from both `Observable[S]` and `Observer[T]` we cannot use the `extend AnyVal` trick +we used for `Observable[T]` and immediately lose transparent interop with Java. + +The problem is even worse because `AsyncSubject`, `BehaviorSubject`, … all derive from `Subject`, +so if we want them to derive from a common base `Subject[T,T]` type in Scala we lose transparency for those as well. +And again, if we expose the various subjects by extending `AnyVal`, they are useless in Scala because they do not inherit +from a common base type. To avoid implementing all methods of observable and observer on each specific subject +we might add implicit conversions to `Observable[T]` and `Observer[T]` but that still does not give Scala users +a native `Subject[S,T]` type. +```scala +object AsyncSubject { + def apply[T](): AsyncSubject[T] = + new AsyncSubject[T](rx.subjects.AsyncSubject.create()) +} -and for Ivy: +class AsyncSubject[T] private [scala] (val inner: rx.subjects.AsyncSubject[T]) + extends AnyVal +{ … } -```xml - +implicit final def asObservable[T](subject: AsyncSubject[T]): Observable[T] = + Observable(subject.inner) + +implicit final def asObserver[T](subject: AsyncSubject[T]): Observer[T] = + subject.inner ``` +The inheritance problem is not just limited to subjects, but also surfaces for subscriptions. +Rx scala currently defines `type Subscription = rx.Subscription` using a type synonym as well, +and we run into exactly the same problems as with subjects when we try to bind the +various Rx subscriptions `BooleanSubscription`, `SerialSubscription`, etc. -and for sbt: +Since we cannot wrap Rx types in Scala such that they are both (a) transparently interoperable with Java, +and (b) feel native and idiomatic to Scala, we should decide in favor of optimizing RxScala for Scala +and consumption of Rx values from Java but not for Scala as a producer. +If we take that approach, we can make bindings that feels like a completely native Scala library, +without needing any complications of the Scala side. ```scala -libraryDependencies ++= Seq( - "com.netflix.rxjava" % "rxjava-scala" % "x.y.z" -) +object Observer { …} +trait Observable[+T] { + def asJavaObservable: rx.Observable[_ <: T] +} + +object Observer {…} +trait Observer[-T] { + def asJavaObserver: rx.Observer[_ >: T] +} + +object Subject {…} +trait Subject[-T, +R] extends Observable[R] with Observer[T] { + val asJavaSubject: rx.subjects.Subject[_ >: T, _<: R] +} + +object Scheduler {…} +trait Scheduler { + def asJavaScheduler: rx.Scheduler; +} + +object Notification {…} +trait Notification[+T] { + def asJavaNotification: rx.Notification[_ <: T] +} + +object Subscription {…} +trait Subscription { + def asJavaSubscription: rx.Subscription +} +``` +You pay the price when crossing the Scala/Java interop boundary, which is where it should be. +The proper way is to put the burden of interop on the Scala side, in case you want to create +a reusable Rx-based library in Scala, or wrap and unwrap on the Java side. +```java +public static void main(String[] args) { + + Observable movies = Observable.from(new Movie(3000), new Movie(1000), new Movie(2000)); + MovieLib lib = new MovieLib(toScalaObservable(movies)); + lib.longMovies().asJavaObservable().subscribe(m -> + System.out.println("A movie of length " + m.lengthInSeconds() + "s") + ); +} ``` +Delegation versus Inheritance +----------------------------- +The obvious thought is that using delegation instead of inheritance (http://c2.com/cgi/wiki?DelegationIsInheritance) +will lead to excessive wrapping, since all Scala types wrap and delegate to an underlying RxJava implementation. +Note however, that the wrapping happens at query generation time and incurs no overhead when messages are flowing +through the pipeline. Say we have a query `xs.map(f).filter(p).subscribe(o)`. Even though the Scala types are wrappers, +the callback that is registered with xs is something like `x => { val y = f(x); if(p(y)){ o.asJavaObserver.onNext(y) }}` +and hence there is no additional runtime penalty. \ No newline at end of file