A typical implementation of {@code subscribe} does the following: - *
- * It stores a reference to the Observer in a collection object, such as a {@code List
- * It returns a reference to the {@link 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 {@link Observer#onCompleted onCompleted} method.
- *
- * An
- *
- * @param observer
- * the observer
- * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving items
- * before the Observable has finished sending them
- * @throws IllegalArgumentException
- * if the {@link Observer} provided as the argument to {@code subscribe()} is {@code null}
+ 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]): Subscription = {
- asJava.subscribe(observer)
+ def subscribe(observer: Observer[T], scheduler: Scheduler): Subscription = {
+ asJavaObservable.subscribe(observer.asJavaObserver, scheduler)
}
-
+
/**
- * An {@link Observer} must call an Observable's {@code subscribe} method in order to
- * receive items and notifications from the Observable.
- *
- * A typical implementation of {@code subscribe} does the following:
- *
- * It stores a reference to the Observer in a collection object, such as a {@code List
- * It returns a reference to the {@link 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 {@link Observer#onCompleted onCompleted} method.
- *
- * An {@code Observable
- *
- * @param observer
- * the observer
- * @param scheduler
- * the {@link Scheduler} on which Observers subscribe to the Observable
- * @return a {@link Subscription} reference with which Observers can stop receiving items and
- * notifications before the Observable has finished sending them
- * @throws IllegalArgumentException
- * if an argument to {@code subscribe()} is {@code null}
+ * $subscribeObserverMain
+ *
+ * @param observer $subscribeObserverParamObserver
+ * @return $subscribeAllReturn
*/
- def subscribe(observer: Observer[T], scheduler: Scheduler): Subscription = {
- asJava.subscribe(observer, scheduler)
+ def subscribe(observer: Observer[T]): Subscription = {
+ 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)
+ )
}
-
- def subscribe(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit): Subscription = {
- asJava.subscribe(onNext, onError, onComplete)
+
+ /**
+ * $subscribeCallbacksMainWithNotifications
+ *
+ * @param onNext $subscribeCallbacksParamOnNext
+ * @param onError $subscribeCallbacksParamOnError
+ * @param onCompleted $subscribeCallbacksParamOnComplete
+ * @return $subscribeAllReturn
+ */
+ def subscribe(onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit): Subscription = {
+ asJavaObservable.subscribe(
+ scalaFunction1ProducingUnitToAction1(onNext),
+ scalaFunction1ProducingUnitToAction1(onError),
+ scalaFunction0ProducingUnitToAction0(onCompleted)
+ )
}
-
- def subscribe(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit, scheduler: Scheduler): Subscription = {
- asJava.subscribe(onNext, onError, onComplete, scheduler)
+
+ /**
+ * $subscribeCallbacksMainWithNotifications
+ *
+ * @param onNext $subscribeCallbacksParamOnNext
+ * @param onError $subscribeCallbacksParamOnError
+ * @param onCompleted $subscribeCallbacksParamOnComplete
+ * @param scheduler $subscribeCallbacksParamScheduler
+ * @return $subscribeAllReturn
+ */
+ def subscribe(onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit, scheduler: Scheduler): Subscription = {
+ asJavaObservable.subscribe(scalaFunction1ProducingUnitToAction1(onNext),
+ scalaFunction1ProducingUnitToAction1(onError),
+ scalaFunction0ProducingUnitToAction0(onCompleted),
+ scheduler)
}
-
+
+ /**
+ * $subscribeCallbacksMainWithNotifications
+ *
+ * @param onNext $subscribeCallbacksParamOnNext
+ * @param onError $subscribeCallbacksParamOnError
+ * @param scheduler $subscribeCallbacksParamScheduler
+ * @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)
}
-
+
+ /**
+ * $subscribeCallbacksMainNoNotifications
+ *
+ * @param onNext $subscribeCallbacksParamOnNext
+ * @param scheduler $subscribeCallbacksParamScheduler
+ * @return $subscribeAllReturn
+ */
def subscribe(onNext: T => Unit, scheduler: Scheduler): Subscription = {
- asJava.subscribe(onNext, scheduler)
+ asJavaObservable.subscribe(scalaFunction1ProducingUnitToAction1(onNext), scheduler)
}
/**
- * Returns a {@link ConnectableObservable} that upon connection 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 {@link Subject} for the {@link ConnectableObservable} to push source items
- * into
- * @param
+ * Returns an Observable that first emits the items emitted by `this`, and then the items emitted
+ * by `that`.
+ *
*
+ *
*
- * A well-behaved Observable does not interleave its invocations of the {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and {@link Observer#onError onError} methods of
- * its {@link Observer}s; it invokes {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after invoking either {@code onCompleted} or {@code onError}.
- * {@code synchronize} enforces this, and the Observable it returns invokes {@code onNext} and {@code onCompleted} or {@code onError} synchronously.
- *
- * @param observable
- * the source Observable
- * @param
+ * Wraps each item emitted by a source Observable in a timestamped tuple.
+ *
* 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 {@link Func0} produces a {@link rx.util.Closing} object. The * {@link Func0} will then
+ *
+ * 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 bufferClosingSelector
- * The {@link Func0} which is used to produce an {@link Observable} for every buffer created.
- * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated buffer
+ *
+ * @param closings
+ * 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 {@link Observable} which produces connected non-overlapping buffers, which are emitted
- * when the current {@link Observable} created with the {@link Func0} argument produces a {@link rx.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(bufferClosingSelector: () => Observable[Closing]) : Observable[Seq[T]] = {
- val f: Func0[_ <: rx.Observable[_ <: Closing]] = bufferClosingSelector().asJava
- val jObs: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(f)
+ def buffer(closings: () => Observable[Closing]) : Observable[Seq[T]] = {
+ 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 "bufferOpenings"
- * Observable produces a {@link rx.util.Opening} object. Additionally the {@link Func0} argument
- * is used to create an Observable which produces {@link rx.util.Closing} objects. When this
+ *
+ * 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 bufferOpenings
- * The {@link Observable} which, when it produces a {@link rx.util.Opening} object, will cause
+ *
+ * @param openings
+ * The [[rx.lang.scala.Observable]] which, when it produces a [[rx.lang.scala.util.Opening]] object, will cause
* another buffer to be created.
- * @param bufferClosingSelector
- * The {@link Func0} which is used to produce an {@link Observable} for every buffer created.
- * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated buffer
+ * @param closings
+ * 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 {@link Observable} which produces buffers which are created and emitted when the specified {@link 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(bufferOpenings: Observable[Opening], bufferClosingSelector: Opening => Observable[Closing]): Observable[Seq[T]] = {
- val opening: rx.Observable[_ <: Opening] = bufferOpenings.asJava
- val closing: Func1[Opening, _ <: rx.Observable[_ <: Closing]] = (o: Opening) => bufferClosingSelector(o).asJava
- val jObs: rx.Observable[_ <: java.util.List[_]] = asJava.buffer(opening, closing)
+ def buffer(openings: Observable[Opening], closings: Opening => Observable[Closing]): Observable[Seq[T]] = {
+ 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"
+ *
+ * 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 {@link Observable} which produces connected non-overlapping buffers containing at most
- * "count" produced values.
+ * 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"
+ *
+ * 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 {@link Observable#buffer(int)}.
+ * 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 {@link Observable} which produces buffers every "skipped" values containing at most
- * "count" produced values.
+ * 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
+ *
+ * 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 {@link 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
+ *
+ * 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 {@link 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 {@link 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
+ * 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 {@link 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]]])
}
/**
* 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
+ * 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 {@link 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 {@link 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]]])
}
/**
* 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
+ * 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 {@link 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
+ * 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 {@link 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 {@link 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
* non-overlapping windows. The current window is emitted and replaced with a new window when the
- * Observable produced by the specified {@link Func0} produces a {@link rx.util.Closing} object. The {@link Func0} will then be used to create a new Observable to listen for the end of the next
+ * 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 closingSelector
- * The {@link Func0} which is used to produce an {@link Observable} for every window created.
- * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated window
+ *
+ * @param closings
+ * 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 {@link Observable} which produces connected non-overlapping windows, which are emitted
- * when the current {@link Observable} created with the {@link Func0} argument produces a {@link rx.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(closingSelector: () => Observable[Closing]): Observable[Observable[T]] = {
- val func : Func0[_ <: rx.Observable[_ <: Closing]] = closingSelector().asJava
- val o1: rx.Observable[_ <: rx.Observable[_]] = asJava.window(func)
- val o2 = new Observable[rx.Observable[_]](o1).map((x: rx.Observable[_]) => {
+ def window(closings: () => Observable[Closing]): Observable[Observable[T]] = {
+ 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)
})
@@ -445,421 +526,426 @@ class Observable[+T](val asJava: rx.Observable[_ <: T])
/**
* Creates an Observable which produces windows of collected values. This Observable produces windows.
- * Chunks are created when the specified "windowOpenings" Observable produces a {@link rx.util.Opening} object.
- * Additionally the {@link Func0} argument is used to create an Observable which produces {@link rx.util.Closing} objects. When this Observable produces such an object, the associated window is
- * emitted.
- *
- * @param windowOpenings
- * The {@link Observable} which when it produces a {@link rx.util.Opening} object, will cause
+ * 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 [[rx.lang.scala.Observable]] which when it produces a [[rx.lang.scala.util.Opening]] object, will cause
* another window to be created.
- * @param closingSelector
- * The {@link Func0} which is used to produce an {@link Observable} for every window created.
- * When this {@link Observable} produces a {@link rx.util.Closing} object, the associated window
+ * @param closings
+ * 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 {@link Observable} which produces windows which are created and emitted when the specified {@link 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(windowOpenings: Observable[Opening], closingSelector: Opening => Observable[Closing]) = {
+ def window(openings: Observable[Opening], closings: Opening => Observable[Closing]) = {
Observable.jObsOfJObsToScObsOfScObs(
- asJava.window(windowOpenings.asJava, (op: Opening) => closingSelector(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
+ * 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 {@link Observable} which produces connected non-overlapping windows containing at most
- * "count" produced values.
+ * 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,
+ * `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 equals that this is the same operation as {@link Observable#window(Observable, int)}.
+ * 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 {@link Observable} which produces windows every "skipped" values containing at most
- * "count" produced values.
+ * 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
+ * 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 {@link 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
+ * 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 {@link 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 {@link 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
+ * 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 {@link 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
+ * 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 {@link 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 {@link 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
+ * 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 {@link 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
+ * 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 {@link 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 {@link 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.
+ *
*
+ * Registers an function to be called when this Observable invokes [[rx.lang.scala.Observer.onCompleted onCompleted]] or [[rx.lang.scala.Observer.onError onError]].
+ *
*
+ *
*
- * Note: {@code mapMany} and {@code flatMap} are equivalent.
- *
- * @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
* item emitted by the source Observable and merging the results of the Observables
* obtained from this transformation.
- * @see #mapMany(Func1)
*/
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 }
+ }))
}
-
- // There is no method like
- // public Observable
+ *
*
+ * 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.
+ *
*
+ * Asynchronously subscribes and unsubscribes Observers on the specified [[rx.lang.scala.Scheduler]].
+ *
*
+ * Asynchronously notify [[rx.lang.scala.Observer]]s on the specified [[rx.lang.scala.Scheduler]].
+ *
*
+ *
+ * This operation is only available if `this` is of type `Observable[Notification[U]]` for some `U`,
+ * otherwise you will get a compilation 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 {@link Observer}, the Observable invokes its Observer's
- *
+ *
* 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 {@link 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 {@link Observer}, the Observable invokes its Observer's
- *
+ *
* 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 {@link Observer#onError onError} if it encounters an error of type {@link java.lang.Exception}.
- *
- * This differs from {@link #onErrorResumeNext} in that this one does not handle {@link java.lang.Throwable} or {@link java.lang.Error} but lets those continue through.
- *
+ * 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 {@link Observer}, the Observable invokes its Observer's
- *
+ *
* 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 {@link 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 {@link Observer}, the Observable invokes its Observer's
- *
+ * `onError` method, it will instead pass the return value of
+ * `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
@@ -868,8 +954,8 @@ class Observable[+T](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
@@ -877,92 +963,88 @@ class Observable[+T](val asJava: rx.Observable[_ <: T])
* by the source Observable into the same function, and so on until all items have been emitted
* by the source Observable, and emits the final result from the final call to your function as
* its sole item.
- *
+ *
*
+ *
* 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
+ * 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]].
+ *
*
+ * 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 {@link 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
- *
+ * 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.
+ *
*
+ *
*
+ *
* 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
+ *
*
+ *
*
+ *
*
+ *
* This sort of function is sometimes called an accumulator.
- *
- * Note that when you pass a seed to
+ *
*
+ *
*
- * You can ignore the first
+ *
*
- * This method returns an Observable that will invoke a subscribing {@link Observer}'s {@link Observer#onNext onNext} function a maximum of
+ *
*
- *
+ *
*
+ * `other` Observable emits an item.
+ *
*
+ *
*
- * Normally, an Observable that returns multiple items will do so by invoking its {@link Observer}'s {@link 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
+ * 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
}
- // corresponds to Java's method
- // public Observable
- *
- *
+ *
*
+ *
*
+ *
* You can combine items emitted by two Observables so that they act like a single
- * Observable by using the {@code merge} method.
- *
+ * Observable by using the `merge` method.
+ *
* @param that
* an Observable to be merged
- * @return an Observable that emits items from {@code this} and {@code that} until
- * {@code this} or {@code that} emits {@code onError} or {@code onComplete}.
+ * @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 [[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.
+ *
+ *
+ *
* NOTE: If events keep firing faster than the timeout then no data will be emitted.
- *
+ *
*
- * Information on debounce vs throttle:
- *
- *
+ *
* NOTE: If events keep firing faster than the timeout then no data will be emitted.
- *
+ *
*
- * Information on debounce vs throttle:
- *
- *
+ *
* NOTE: If events keep firing faster than the timeout then no data will be emitted.
- *
+ *
*
- * Information on debounce vs throttle:
- *
- *
+ *
* NOTE: If events keep firing faster than the timeout then no data will be emitted.
- *
+ *
*
- * This differs from {@link #throttleLast} in that this only tracks passage of time whereas {@link #throttleLast} ticks at scheduled intervals.
- *
+ *
+ * This differs from `Observable.throttleLast` in that this only tracks passage of time whereas `Observable.throttleLast` ticks at scheduled intervals.
+ *
*
- * This differs from {@link #throttleLast} in that this only tracks passage of time whereas {@link #throttleLast} ticks at scheduled intervals.
- *
+ *
+ * This differs from `Observable.throttleLast` in that this only tracks passage of time whereas `Observable.throttleLast` ticks at scheduled intervals.
+ *
*
- * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas {@link #throttleFirst} does not tick, it just tracks passage of time.
- *
+ *
+ * This differs from `Observable.throttleFirst` in that this ticks along at a scheduled interval whereas `Observable.throttleFirst` does not tick, it just tracks passage of time.
+ *
*
- * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas {@link #throttleFirst} does not tick, it just tracks passage of time.
- *
+ *
+ * This differs from `Observable.throttleFirst` in that this ticks along at a scheduled interval whereas `Observable.throttleFirst` does not tick, it just tracks passage of time.
+ *
*
+ * Creates an Observable that will execute the given function when an [[rx.lang.scala.Observer]] subscribes to it.
+ *
*
- * Write the function you pass to
- * A well-formed Observable must invoke either the Observer's
+ *
+ * A well-formed Observable must invoke either the Observer's `onCompleted` method
+ * exactly once or its `onError` method exactly once.
+ *
* See Rx Design Guidelines (PDF)
* for detailed information.
- *
- * @param
+ * Returns an Observable that invokes an [[rx.lang.scala.Observer]]'s [[rx.lang.scala.Observer.onError onError]] method when the Observer subscribes to it
+ *
*
+ *
* Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link 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
- * @param
+ *
*
+ *
* 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 {@link 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 {@link Observer} that
+ *
+ * @param observable
+ * the Observable factory function to invoke for each [[rx.lang.scala.Observer]] that
* subscribes to the resulting Observable
- * @param
- *
- * To convert any object into an Observable that emits that object, pass that object into the
- *
- * This is similar to the {@link #apply(Iterable[T])} method, except that
- * {@link #apply(Iterable[T])} will convert an {@link Iterable} object into an Observable that emits
- * each of the items in the Iterable, one at a time, while the
- *
- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its
- * Observers once.
- *
- * 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.
- *
- * @param source
- * a list of Observables
- * @return an Observable that emits items that are the result of flattening the items emitted by
- * the {@code source} list of Observables
- * @see MSDN: Observable.Merge Method
- */
- // public static
- *
- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its
- * Observers once.
- *
- * 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.
- *
- * @param source
- * an Observable that emits Observables
- * @return an Observable that emits items that are the result of flattening the items emitted by
- * the Observables emitted by the {@code source} Observable
- * @see MSDN: Observable.Merge Method
- */
- // public static
- *
- * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its
- * Observers once.
- *
- * 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.
- *
- * @param source
- * a series of Observables
- * @return an Observable that emits items that are the result of flattening the items emitted by
- * the {@code source} Observables
- * @see MSDN: Observable.Merge Method
- */
- // public static
+ * 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 {@link 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())
}
-
- // There is no method corresponding to
- // public static
- *
+ *
+ *
+ * Observable<T>
instance is responsible for accepting all subscriptions
- * and notifying all Observers. Unless the documentation for a particular
- * Observable<T>
implementation indicates otherwise, Observers should make no
- * assumptions about the order in which multiple Observers will receive their notifications.
- *
- *
+ *
* @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(_.asJavaObservable)
+ val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable
+ val o5 = rx.Observable.concat[U](o4)
+ Observable[U](o5)
}
-
/**
* Wraps this Observable in another Observable that ensures that the resulting
* Observable is chronologically well-behaved.
- *
- *
- *
+ *
* @return an Observable that emits timestamped items from the source Observable
*/
- def timestamp: Observable[Timestamped[T]] = {
- Observable[Timestamped[T]](asJava.timestamp())
+ def timestamp: Observable[(Long, T)] = {
+ 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.
- * The number of {@code onNext} invocations of the resulting {@code Observable[(T, U)]}
- * is the minumum of the number of {@code onNext} invocations of {@code this} and {@code that}.
+ * The number of `onNext` invocations of the resulting `Observable[(T, U)]`
+ * 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)))
- }
-
- // There is no method corresponding to
- // public static
- *
+ *
* @param predicate
- * a function that evaluates the items emitted by the source Observable, returning {@code true} if they pass the filter
+ * 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 {@code true}
+ * evaluates as `true`
*/
def filter(predicate: T => Boolean): Observable[T] = {
- Observable[T](asJava.filter(predicate))
+ Observable[T](asJavaObservable.filter(predicate))
}
/**
- * Registers an {@link Action0} to be called when this Observable invokes {@link Observer#onCompleted onCompleted} or {@link Observer#onError onError}.
- *
- *
+ *
* @param action
- * an {@link Action0} to be invoked when the source Observable finishes
- * @return an Observable that emits the same items as the source Observable, then invokes the {@link Action0}
- * @see MSDN: Observable.Finally Method
+ * 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
* the source Observable, where that function returns an Observable, and then merging those
* resulting Observables and emitting the results of this merger.
- *
- *
- *
+ *
* @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)
+ }))
}
-
- // There's no method like
- // public
- *
+ *
* @return an Observable whose items are the result of materializing the items and
* notifications of the source Observable
- * @see MSDN: Observable.materialize
*/
def materialize: Observable[Notification[T]] = {
- Observable[Notification[T]](asJava.materialize())
+ Observable[rx.Notification[_ <: T]](asJavaObservable.materialize()).map(Notification(_))
}
/**
- * Asynchronously subscribes and unsubscribes Observers on the specified {@link Scheduler}.
- *
- *
+ *
* @param scheduler
- * the {@link 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 {@link 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 {@link Observer}s on the specified {@link Scheduler}.
- *
- *
+ *
* @param scheduler
- * the {@link Scheduler} to notify {@link Observer}s on
- * @return the source Observable modified so that its {@link Observer}s are notified on the
- * specified {@link 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 {@link #materialize materialize} by
- * transforming the {@link 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.
- *
- *
- * @return an Observable that emits the items and notifications embedded in the {@link 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: T <:< Notification[U]): Observable[U] = {
- val o = asJava.dematerialize[U]()
- Observable[U](o)
+ 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.asJavaObservable.dematerialize[U]()
+ Observable[U](o3)
}
-
+
/**
- * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error.
- *
- *
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 {@link 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
+ * 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
+ * [[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.
- *
- *
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 {@link 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
+ * 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 [[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.
- *
- *
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 {@link 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
+ * 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 [[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.
- *
- *
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
+ * 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 {@link Observer#onNext onNext} method.
- *
- *
inject
method that does a similar operation on lists.
- *
+ * 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
- * @see MSDN: Observable.Aggregate
- * @see Wikipedia: Fold (higher-order function)
*/
- 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 {@link ConnectableObservable} that shares a single subscription to the underlying
- * Observable that will replay all of its items and notifications to any future {@link Observer}.
- *
- *
- * @return a {@link ConnectableObservable} that upon connection causes the source Observable to
- * emit items to its {@link 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(): ConnectableObservable[T] = {
- new ConnectableObservable[T](asJava.replay())
+ def replay: (() => Subscription, Observable[T]) = {
+ val javaCO = asJavaObservable.replay()
+ (() => javaCO.connect(), Observable[T](javaCO))
}
/**
- * This method has similar behavior to {@link #replay} except that this auto-subscribes to
- * the source Observable rather than returning a {@link ConnectableObservable}.
- *
- *
cache()
operator so be careful not to use this operator on Observables that
+ * `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 {@link ConnectableObservable}, which waits until its {@link ConnectableObservable#connect connect} method is called before it begins emitting
- * items to those {@link Observer}s that have subscribed to it.
- *
- *
- * @return a {@link ConnectableObservable} that upon connection causes the source Observable to
- * emit items to its {@link 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: ConnectableObservable[T] = {
- new ConnectableObservable[T](asJava.publish())
+ def publish: (() => Subscription, Observable[T]) = {
+ val javaCO = asJavaObservable.publish()
+ (() => javaCO.connect(), Observable[T](javaCO))
}
- // There is no aggregate function with signature
- // public Observable
- *
inject
method that does a similar operation on lists.
- *
+ * has an `inject` method that does a similar operation on lists.
+ *
* @param initialValue
* the initial (seed) accumulator value
* @param accumulator
@@ -970,452 +1052,461 @@ class Observable[+T](val asJava: rx.Observable[_ <: T])
* Observable, the result of which 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 items emitted by the source Observable
- * @see MSDN: Observable.Aggregate
- * @see Wikipedia: Fold (higher-order function)
*/
- def fold[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = {
- Observable[R](asJava.reduce(initialValue, accumulator))
+ def foldLeft[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = {
+ Observable[R](asJavaObservable.reduce(initialValue, new Func2[R,T,R]{
+ def call(t1: R, t2: T): R = accumulator(t1,t2)
+ }))
}
- // corresponds to Java's
- // public
- *
- * @param period
- * the sampling rate
- * @param unit
- * the {@link 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))
}
/**
* 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 {@link TimeUnit} in which
period
is defined
+ *
+ * @param duration the sampling rate
* @param scheduler
- * the {@link 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
* by an Observable into the same function, and so on until all items have been emitted by the
* source Observable, emitting the result of each of these iterations.
- *
- *
scan()
the resulting Observable will emit
+ *
+ * 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 {@link Observer}s via {@link 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
- * @see MSDN: Observable.Scan
*/
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)
+ }))
}
- // corresponds to Scala's
- // public
- *
+ *
* @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
- * Observable satisfy the predicate; otherwise, false
+ * @return an Observable that emits `true` if all items emitted by the source
+ * Observable satisfy the predicate; otherwise, `false`
*/
def forall(predicate: T => Boolean): Observable[Boolean] = {
// type mismatch; found : rx.Observable[java.lang.Boolean] required: rx.Observable[_ <: scala.Boolean]
// new Observable[Boolean](asJava.all(predicate))
// it's more fun in Scala:
- this.map(predicate).fold(true)(_ && _)
+ this.map(predicate).foldLeft(true)(_ && _)
}
- // corresponds to Java's
- // public Observablenum
items emitted by the source
+ * Returns an Observable that skips the first `num` items emitted by the source
* Observable and emits the remainder.
- *
- *
num
items emitted by an Observable and attend only to
- * those items that come after, by modifying the Observable with the skip
method.
- *
- * @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
+ * 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))
+ }
+
+ /**
+ * Returns an Observable that bypasses all items from the source Observable as long as the specified
+ * condition holds true. Emits all further source items as soon as the condition becomes false.
+ *
+ *
+ *
+ * @param predicate
+ * A function to test each item emitted from the source Observable for a condition.
+ * @return an Observable that emits all items from the source Observable as soon as the condition
+ * becomes false.
+ */
+ def dropWhile(predicate: T => Boolean): Observable[T] = {
+ Observable(asJavaObservable.skipWhile(predicate))
}
- // corresponds to Java's
- // public Observable
num
items emitted by the source
+ * Returns an Observable that emits only the first `num` items emitted by the source
* Observable.
- *
- *
num
times before invoking
- * {@link 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
+ * @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
+ * fewer than `num` items
*/
def take(n: Int): Observable[T] = {
- Observable[T](asJava.take(n))
+ Observable[T](asJavaObservable.take(n))
}
/**
* Returns an Observable that emits items emitted by the source Observable so long as a
* specified condition is true.
- *
- *
+ *
* @param predicate
* a function that evaluates an item emitted by the source Observable and returns a
* Boolean
* @return an Observable that emits the items from the source Observable so long as each item
- * satisfies the condition defined by
predicate
+ * satisfies the condition defined by `predicate`
*/
def takeWhile(predicate: T => Boolean): Observable[T] = {
- Observable[T](asJava.takeWhile(predicate))
- }
-
- /**
- * Returns an Observable that emits the items emitted by a source Observable so long as a given
- * predicate remains true, where the predicate can operate on both the item and its index
- * relative to the complete sequence.
- *
- *
- * @param predicate
- * a function to test each item emitted by the source Observable for a condition;
- * the second parameter of the function represents the index of the source item
- * @return an Observable that emits items from the source Observable so long as the predicate
- * continues to return
true
for each item, then completes
- */
- // TODO: if we have zipWithIndex, takeWhileWithIndex is not needed any more
- def takeWhileWithIndex(predicate: (T, Integer) => Boolean): Observable[T] = {
- Observable[T](asJava.takeWhileWithIndex(predicate))
- }
-
- /* TODO zipWithIndex once it's in RxJava
- def zipWithIndex: Observable[(T, Int)] = {
- ???
+ Observable[T](asJavaObservable.takeWhile(predicate))
}
- */
/**
- * Returns an Observable that emits only the last count
items emitted by the source
+ * Returns an Observable that emits only the last `count` items emitted by the source
* 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
+ * @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))
}
- // corresponds to Java's
- // public Observableother
Observable emits an item.
- *
- *
+ *
* @param that
- * the Observable whose first emitted item will cause
takeUntil
to stop
+ * the Observable whose first emitted item will cause `takeUntil` to stop
* emitting items from the source Observable
- * @param other
+ * @tparam E
+ * the type of items emitted by `other`
* @return an Observable that emits the items of the source Observable until such time as
- * other
emits its first item
+ * `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.
- *
- *
onNext
function once, passing it the entire list, by
- * calling the Observable's toList
method prior to calling its {@link #subscribe} method.
- * > toList() {
- // There are no toSortedList methods because Scala can sort itself
- // public Observable
> toSortedList()
- // public Observable
> toSortedList(Func2 super T, ? super T, Integer> sortFunction)
-
- // There is no method
- // def startWith[U >: T](values: U*): Observable[U]
- // because we can just use ++ instead
-
/**
- * Groups the items emitted by an Observable according to a specified criterion, and emits these
- * grouped items as {@link GroupedObservable}s, one GroupedObservable per group.
- *
- *
- * @param keySelector
+ * Groups the items emitted by this Observable according to a specified discriminator function.
+ *
+ * @param f
* a function that extracts the key from an item
- * @param elementSelector
- * a function to map a source item to an item in a {@link GroupedObservable}
- * @param
- *
- * @param keySelector
- * a function that extracts the key for each item
- * @param
- *
- * @param sequenceOfSequences
- * the source Observable that emits Observables
+ *
+ * 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 only the items emitted by the most recently published
* Observable
+ *
+ * @usecase def switch[U]: Observable[U]
+ * @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)
}
- // TODO naming: follow C# (switch) or Java (switchOnNext)?
- // public static
- *
+ *
+ * Even if multiple merged Observables send `onError` notifications, `mergeDelayError` will only invoke the `onError` method of its
+ * Observers once.
+ *
+ * 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.
+ *
+ * @param that
+ * an Observable to be merged
+ * @return an Observable that emits items that are the result of flattening the items emitted by
+ * `this` and `that`
+ */
+ def mergeDelayError[U >: T](that: Observable[U]): Observable[U] = {
+ Observable[U](rx.Observable.mergeDelayError[U](this.asJavaObservable, that.asJavaObservable))
+ }
+
+ /**
+ * Flattens the sequence of Observables emitted by `this` into one Observable, without any
+ * transformation.
+ *
+ *
+ *
+ * You can combine the items emitted by multiple Observables so that they act like a single
+ * Observable by using this method.
+ *
+ * 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
+ */
+ 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(_.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 [[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.
+ *
+ *
+ *
+ * Even if multiple merged Observables send `onError` notifications, this method will only invoke the `onError` method of its
+ * Observers once.
+ *
+ * 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(_.asJavaObservable)
+ val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable
+ val o5 = rx.Observable.mergeDelayError[U](o4)
+ Observable[U](o5)
+ }
+
+ /**
+ * Combines two observables, emitting a pair of the latest values of each of
+ * the source observables each time an event is received from one of the source observables, where the
+ * aggregation is defined by the given function.
+ *
+ * @param that
+ * The second source observable.
+ * @return An Observable that combines the source Observables
+ */
+ 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.asJavaObservable, that.asJavaObservable, f))
+ }
+
/**
* Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call.
- *
- *
- *
+ *
+ * $debounceVsThrottle
*
* @param timeout
- * The time each value has to be 'the most recent' of the {@link 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 {@link Observable} which filters out values which are too quickly followed up with newer values.
- * @see {@link #debounce}
+ * @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))
}
/**
* Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call.
- *
- *
- *
+ *
+ * $debounceVsThrottle
*
* @param timeout
- * The time each value has to be 'the most recent' of the {@link 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 {@link Observable} which filters out values which are too quickly followed up with newer values.
- * @see {@link #throttleWithTimeout};
+ * @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))
}
/**
* Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call.
- *
- *
- *
+ *
+ * $debounceVsThrottle
*
* @param timeout
- * The time each value has to be 'the most recent' of the {@link 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 {@link 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 {@link #throttleWithTimeout};
+ * @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))
}
/**
* Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call.
- *
*
* @param timeout
- * The time each value has to be 'the most recent' of the {@link 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 {@link 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 {@link #debounce}
+ * @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))
}
/**
* Throttles by skipping value until `skipDuration` passes and then emits the next received value.
- *
*
* @param skipDuration
* Time to wait before sending another value after emitting last value.
* @param scheduler
- * The {@link 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))
}
/**
* Throttles by skipping value until `skipDuration` passes and then emits the next received value.
- *
*
* @param skipDuration
@@ -1423,436 +1514,541 @@ class Observable[+T](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))
}
/**
* Throttles by returning the last value of each interval defined by 'intervalDuration'.
- *
*
* @param intervalDuration
* Duration of windows within with the last value will be chosen.
* @return Observable which performs the throttle operation.
- * @see {@link #sample(long, TimeUnit)}
*/
def throttleLast(intervalDuration: Duration): Observable[T] = {
- Observable[T](asJava.throttleLast(intervalDuration.length, intervalDuration.unit))
+ Observable[T](asJavaObservable.throttleLast(intervalDuration.length, intervalDuration.unit))
}
/**
* Throttles by returning the last value of each interval defined by 'intervalDuration'.
- *
*
* @param intervalDuration
* Duration of windows within with the last value will be chosen.
* @return Observable which performs the throttle operation.
- * @see {@link #sample(long, TimeUnit, Scheduler)}
*/
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
+ */
+ def product[U >: T](implicit num: Numeric[U]): Observable[U] = {
+ foldLeft(num.one)(num.times)
+ }
+
+ /**
+ * Returns an Observable that emits only the very first item emitted by the source Observable, or
+ * a default value if the source Observable is empty.
+ *
+ *
+ *
+ * @param default
+ * The default value to emit if the source Observable doesn't emit anything.
+ * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything.
+ * @return an Observable that emits only the very first item from the source, or a default value
+ * if the source Observable completes without emitting any item.
+ */
+ def firstOrElse[U >: T](default: => U): Observable[U] = {
+ this.take(1).foldLeft[Option[U]](None)((v: Option[U], e: U) => Some(e)).map({
+ case Some(element) => element
+ case None => default
+ })
+ }
+
+ /**
+ * Returns an Observable that emits only the very first item emitted by the source Observable, or
+ * a default value if the source Observable is empty.
+ *
+ *
+ *
+ * @param default
+ * The default value to emit if the source Observable doesn't emit anything.
+ * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything.
+ * @return an Observable that emits only the very first item from the source, or a default value
+ * if the source Observable completes without emitting any item.
+ */
+ def headOrElse[U >: T](default: => U): Observable[U] = firstOrElse(default)
+
+ /**
+ * Returns an Observable that emits only the very first item emitted by the source Observable.
+ * This is just a shorthand for `take(1)`.
+ *
+ *
+ *
+ * @return an Observable that emits only the very first item from the source, or none if the
+ * 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
+
+ /**
+ * emits NoSuchElementException("head of empty Observable") if empty
+ */
+ def head: Observable[T] = {
+ this.take(1).fold[Option[T]](None)((v: Option[T], e: T) => Some(e)).map({
+ case Some(element) => element
+ case None => throw new NoSuchElementException("head of empty Observable")
+ })
}
/**
- * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking
+ * emits an UnsupportedOperationException("tail of empty list") if empty
+ */
+ def tail: Observable[T] = ???
+
+ */
+
+ /**
+ * Returns an Observable that forwards all sequentially distinct items emitted from the source Observable.
+ *
+ *
+ *
+ * @return an Observable of sequentially distinct items
+ */
+ def distinctUntilChanged: Observable[T] = {
+ Observable[T](asJavaObservable.distinctUntilChanged)
+ }
+
+ /**
+ * Returns an Observable that forwards all items emitted from the source Observable that are sequentially
+ * distinct according to a key selector function.
+ *
+ *
+ *
+ * @param keySelector
+ * a function that projects an emitted item to a key value which is used for deciding whether an item is sequentially
+ * distinct from another one or not
+ * @return an Observable of sequentially distinct items
+ */
+ def distinctUntilChanged[U](keySelector: T => U): Observable[T] = {
+ Observable[T](asJavaObservable.distinctUntilChanged[U](keySelector))
+ }
+
+ /**
+ * Returns an Observable that forwards all distinct items emitted from the source Observable.
+ *
+ *
+ *
+ * @return an Observable of distinct items
+ */
+ def distinct: Observable[T] = {
+ Observable[T](asJavaObservable.distinct())
+ }
+
+ /**
+ * Returns an Observable that forwards all items emitted from the source Observable that are distinct according
+ * to a key selector function.
+ *
+ *
+ *
+ * @param keySelector
+ * a function that projects an emitted item to a key value which is used for deciding whether an item is
+ * distinct from another one or not
+ * @return an Observable of distinct items
+ */
+ def distinct[U](keySelector: T => U): Observable[T] = {
+ Observable[T](asJavaObservable.distinct[U](keySelector))
+ }
+
+ /**
+ * Returns an Observable that counts the total number of elements in the source Observable.
+ *
+ *
+ *
+ * @return an Observable emitting the number of counted elements of the source Observable
+ * as its single item.
+ */
+ def length: Observable[Int] = {
+ Observable[Integer](asJavaObservable.count()).map(_.intValue())
+ }
+
+ /**
+ * Returns an Observable that counts the total number of elements in the source Observable.
+ *
+ *
+ *
+ * @return an Observable emitting the number of counted elements of the source Observable
+ * as its single item.
+ */
+ def size: Observable[Int] = length
+
+ /**
+ * Retry subscription to origin Observable upto given retry count.
+ *
+ *
+ *
+ * If [[rx.lang.scala.Observer.onError]] is invoked the source Observable will be re-subscribed to as many times as defined by retryCount.
+ *
+ * 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].
+ *
+ * @param retryCount
+ * Number of retry attempts before failing.
+ * @return Observable with retry logic.
+ */
+ def retry(retryCount: Int): Observable[T] = {
+ Observable[T](asJavaObservable.retry(retryCount))
+ }
+
+ /**
+ * Retry subscription to origin Observable whenever onError is called (infinite retry count).
+ *
+ *
+ *
+ * If [[rx.lang.scala.Observer.onError]] is invoked the source Observable will be re-subscribed to.
+ *
+ * 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](asJavaObservable.retry())
+ }
+
+ /**
+ * Converts an Observable into a [[rx.lang.scala.observables.BlockingObservable]] (an Observable with blocking
* operators).
- *
+ *
* @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]]
+ * [[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 [[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)).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 [[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 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)).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.
+ */
+ def exists(p: T => Boolean): Observable[Boolean] = {
+ 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.
+ */
+ def isEmpty: Observable[Boolean] = {
+ Observable[java.lang.Boolean](asJavaObservable.isEmpty).map(_.booleanValue())
+ }
+
def withFilter(p: T => Boolean): WithFilter[T] = {
- new WithFilter[T](p, asJava)
+ new WithFilter[T](p, asJavaObservable)
}
-
+
}
+/**
+ * Provides various ways to construct new Observables.
+ */
object Observable {
import scala.collection.JavaConverters._
import scala.collection.immutable.Range
import scala.concurrent.duration.Duration
- import scala.concurrent.Future
- import rx.{Observable => JObservable}
- import rx.lang.scala.{Notification, Subscription, Scheduler, Observer}
- import rx.lang.scala.util._
- import rx.lang.scala.internal.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})
}
-
- def apply[T](asJava: rx.Observable[_ <: T]): Observable[T] = {
- new Observable[T](asJava)
+
+ /**
+ * Creates a new Scala Observable from a given Java Observable.
+ */
+ 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 {@link Observer} subscribes to it.
- *
- *
create
so that it behaves as an Observable: It
- * should invoke the Observer's {@link Observer#onNext onNext}, {@link Observer#onError onError}, and {@link Observer#onCompleted onCompleted} methods
+ *
+ * Write the function you pass to `create` so that it behaves as an Observable: It
+ * 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.
- * onCompleted
method
- * exactly once or its onError
method exactly once.
- *
- *
+ *
* @param exception
* the particular error to report
- * @param
- *
- *
+ *
+ * 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))
}
-
- // There is no method corresponding to
- // public static Observable
- *
- *
just
method.
- * just()
method
- * converts an Iterable into an Observable that emits the entire Iterable as a single item.
- *
- * @param value
- * the item to pass to the {@link Observer}'s {@link Observer#onNext onNext} method
- * @param
- *
- *
- *
- *
- *
- * @param o1
- * The first source observable.
- * @param o2
- * The second source observable.
- * @param combineFunction
- * The aggregation function used to combine the source observable values.
- * @return An Observable that combines the source Observables with the given combine function
+
+ /**
+ * 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
+ * `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
*/
- // public static
+ *
+ * @param duration
+ * duration between two consecutive numbers
+ * @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*/
}
-}
-// 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."
-class WithFilter[+T] private[scala] (p: T => Boolean, asJava: rx.Observable[_ <: T]) {
- import rx.lang.scala.internal.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)))
+ /**
+ * Emits `0`, `1`, `2`, `...` with a delay of `duration` between consecutive numbers.
+ *
+ *
+ *
+ * @param duration
+ * duration between two consecutive numbers
+ * @param scheduler
+ * the scheduler to use
+ * @return An Observable that emits a number each time interval.
+ */
+ def interval(duration: Duration, scheduler: Scheduler): Observable[Long] = {
+ Observable[java.lang.Long](rx.Observable.interval(duration.length, duration.unit, scheduler)).map(_.longValue())
+ /*XXX*/
}
-
- // there is no foreach here, that's only available on BlockingObservable
-}
-class UnitTestSuite extends JUnitSuite {
- import scala.concurrent.duration._
- import org.junit.{Before, Test, Ignore}
- import org.junit.Assert._
- import org.mockito.Matchers.any
- 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 def testTest() = {
- val a: Observable[Int] = Observable()
- assertEquals(4, Observable(1, 2, 3, 4).toBlockingObservable.last)
- }
-
}
+
+
+
+
+
+
diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala
new file mode 100644
index 0000000000..73f865ef84
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala
@@ -0,0 +1,62 @@
+/**
+ * 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 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/rx/lang/scala/RxImplicits.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala
deleted file mode 100644
index d0d33954b7..0000000000
--- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala
+++ /dev/null
@@ -1,132 +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
-
-/**
- * This is the old Scala adaptor. It is kept here for backwards compatibility.
- * The new adaptor is {@code rx.lang.scala.Observable}.
- */
-@deprecated("use rx.lang.scala.Observable instead", "0.14")
-object RxImplicits {
- import java.{ lang => jlang }
- import language.implicitConversions
-
- import rx.{ Observable, Observer, Subscription }
- import rx.Observable.OnSubscribeFunc
- import rx.observables.BlockingObservable
- import rx.util.functions._
-
- /**
- * Converts 0-arg function to Rx Action0
- */
- implicit def scalaFunction0ProducingUnitToAction0(f: (() => Unit)): Action0 =
- new Action0 {
- def call(): Unit = f()
- }
-
- /**
- * Converts 1-arg function to Rx Action1
- */
- implicit def scalaFunction1ProducingUnitToAction1[A](f: (A => Unit)): Action1[A] =
- new Action1[A] {
- def call(a: A): Unit = f(a)
- }
-
- /**
- * Converts 1-arg predicate to Rx Func1[A, java.lang.Boolean]
- */
- implicit def scalaBooleanFunction1ToRxBooleanFunc1[A](f: (A => Boolean)): Func1[A, jlang.Boolean] =
- new Func1[A, jlang.Boolean] {
- def call(a: A): jlang.Boolean = f(a).booleanValue
- }
-
- /**
- * Converts a specific function shape (used in takeWhile) to the equivalent Java types with an Rx Func2
- */
- implicit def convertTakeWhileFuncToRxFunc2[A](f: (A, Int) => Boolean): Func2[A, jlang.Integer, jlang.Boolean] =
- new Func2[A, jlang.Integer, jlang.Boolean] {
- def call(a: A, b: jlang.Integer): jlang.Boolean = f(a, b).booleanValue
- }
-
- /**
- * Converts a function shaped like compareTo into the equivalent Rx Func2
- */
- implicit def convertComparisonFuncToRxFunc2[A](f: (A, A) => Int): Func2[A, A, jlang.Integer] =
- new Func2[A, A, jlang.Integer] {
- def call(a1: A, a2: A): jlang.Integer = f(a1, a2).intValue
- }
-
- /*
- * This implicit allows Scala code to use any exception type and still work
- * with invariant Func1 interface
- */
- implicit def exceptionFunction1ToRxExceptionFunc1[A <: Exception, B](f: (A => B)): Func1[Exception, B] =
- new Func1[Exception, B] {
- def call(ex: Exception): B = f(ex.asInstanceOf[A])
- }
-
- /**
- * The following implicits convert functions of different arities into the Rx equivalents
- */
- implicit def scalaFunction0ToRxFunc0[A](f: () => A): Func0[A] =
- new Func0[A] {
- def call(): A = f()
- }
-
- implicit def scalaFunction1ToRxFunc1[A, B](f: (A => B)): Func1[A, B] =
- new Func1[A, B] {
- def call(a: A): B = f(a)
- }
-
- implicit def scalaFunction2ToRxFunc2[A, B, C](f: (A, B) => C): Func2[A, B, C] =
- new Func2[A, B, C] {
- def call(a: A, b: B) = f(a, b)
- }
-
- implicit def scalaFunction3ToRxFunc3[A, B, C, D](f: (A, B, C) => D): Func3[A, B, C, D] =
- new Func3[A, B, C, D] {
- def call(a: A, b: B, c: C) = f(a, b, c)
- }
-
- implicit def scalaFunction4ToRxFunc4[A, B, C, D, E](f: (A, B, C, D) => E): Func4[A, B, C, D, E] =
- new Func4[A, B, C, D, E] {
- def call(a: A, b: B, c: C, d: D) = f(a, b, c, d)
- }
-
- implicit def onSubscribeFunc[A](f: (Observer[_ >: A]) => Subscription): OnSubscribeFunc[A] =
- new OnSubscribeFunc[A] {
- override def onSubscribe(a: Observer[_ >: A]) = f(a)
- }
-
- /**
- * This implicit class implements all of the methods necessary for including Observables in a
- * for-comprehension. Note that return type is always Observable, so that the ScalaObservable
- * type never escapes the for-comprehension
- */
- implicit class ScalaObservable[A](wrapped: Observable[A]) {
- def map[B](f: A => B): Observable[B] = wrapped.map[B](f)
- def flatMap[B](f: A => Observable[B]): Observable[B] = wrapped.mapMany(f)
- def foreach(f: A => Unit): Unit = wrapped.toBlockingObservable.forEach(f)
- def withFilter(p: A => Boolean): WithFilter = new WithFilter(p)
-
- class WithFilter(p: A => Boolean) {
- def map[B](f: A => B): Observable[B] = wrapped.filter(p).map(f)
- def flatMap[B](f: A => Observable[B]): Observable[B] = wrapped.filter(p).flatMap(f)
- def foreach(f: A => Unit): Unit = wrapped.filter(p).toBlockingObservable.forEach(f)
- def withFilter(p: A => Boolean): Observable[A] = wrapped.filter(p)
- }
- }
-}
\ No newline at end of file
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
new file mode 100644
index 0000000000..4c84eed840
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala
@@ -0,0 +1,226 @@
+/**
+ * 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 ImplicitFunctionConversions.scalaFunction0ProducingUnitToAction0
+import ImplicitFunctionConversions.schedulerActionToFunc2
+import rx.util.functions.{Action0, Action1, Func2}
+
+/**
+ * Represents an object thatimport rx.lang.scala.ImplicitFunctionConversions
+ 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.call() }
+ }
+ }))
+ //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/rx/lang/scala/Subscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala
new file mode 100644
index 0000000000..bd3c6849a9
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala
@@ -0,0 +1,78 @@
+/**
+ * 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
+}
+
+object Subscription {
+ import java.util.concurrent.atomic.AtomicBoolean
+ import rx.lang.scala.subscriptions._
+
+ /**
+ * 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 unsubscribed = new AtomicBoolean(false)
+ def isUnsubscribed = unsubscribed.get()
+
+ val asJavaSubscription = new rx.Subscription {
+ def unsubscribe() { if(!unsubscribed.get()) { u ; unsubscribed.set(true) }}
+ }
+ }
+ }
+
+}
+
+
+
+
+
+
diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala
new file mode 100644
index 0000000000..729450d2a5
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala
@@ -0,0 +1,41 @@
+/**
+ * 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 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."
+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/rx/lang/scala/concurrency/Schedulers.scala
new file mode 100644
index 0000000000..cbba7fd1db
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/Schedulers.scala
@@ -0,0 +1,77 @@
+/**
+ * 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
+import java.util.concurrent.ScheduledExecutorService
+import rx.lang.scala.Scheduler
+import rx.lang.scala.ImplicitFunctionConversions._
+
+/**
+ * Factory methods for creating Schedulers.
+ */
+object Schedulers {
+
+ /**
+ * Returns a [[rx.lang.scala.Scheduler]] that executes work immediately on the current thread.
+ */
+ def immediate: Scheduler = Scheduler(rx.concurrency.Schedulers.immediate())
+
+ /**
+ * Returns a [[rx.lang.scala.Scheduler]] that queues work on the current thread to be executed after the current work completes.
+ */
+ def currentThread: Scheduler = Scheduler(rx.concurrency.Schedulers.currentThread())
+
+ /**
+ * Returns a [[rx.lang.scala.Scheduler]] that creates a new {@link Thread} for each unit of work.
+ */
+ def newThread: Scheduler = Scheduler(rx.concurrency.Schedulers.newThread)
+
+ /**
+ * Returns a [[rx.lang.scala.Scheduler]] that queues work on an `java.util.concurrent.Executor`.
+ *
+ * Note that this does not support scheduled actions with a delay.
+ */
+ def executor(executor: Executor): Scheduler = Scheduler(rx.concurrency.Schedulers.executor(executor))
+
+ /**
+ * Returns a [[rx.lang.scala.Scheduler]] that queues work on an `java.util.concurrent.ScheduledExecutorService`.
+ */
+ def executor(executor: ScheduledExecutorService): Scheduler = Scheduler(rx.concurrency.Schedulers.executor(executor))
+
+ /**
+ * Returns a [[rx.lang.scala.Scheduler]] intended for computational work.
+ *
+ * The implementation is backed by a `java.util.concurrent.ScheduledExecutorService` thread-pool sized to the number of CPU cores.
+ *
+ * This can be used for event-loops, processing callbacks and other computational work.
+ *
+ * Do not perform IO-bound work on this scheduler. Use [[rx.lang.scala.concurrency.Schedulers.threadPoolForIO]] instead.
+ */
+ def threadPoolForComputation: Scheduler = Scheduler(rx.concurrency.Schedulers.threadPoolForComputation())
+
+ /**
+ * [[rx.lang.scala.Scheduler]] intended for IO-bound work.
+ *
+ * The implementation is backed by an `java.util.concurrent.Executor` thread-pool that will grow as needed.
+ *
+ * This can be used for asynchronously performing blocking IO.
+ *
+ * Do not perform computational work on this scheduler. Use [[rx.lang.scala.concurrency.Schedulers.threadPoolForComputation]] instead.
+ */
+ def threadPoolForIO: Scheduler = Scheduler(rx.concurrency.Schedulers.threadPoolForIO())
+
+}
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
new file mode 100644
index 0000000000..f7b7e4beba
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/concurrency/TestScheduler.scala
@@ -0,0 +1,82 @@
+/**
+ * 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
+
+/**
+ * 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
+ }
+}
+
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
index 9dabd3356b..a3e61c0021 100644
--- 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
@@ -17,18 +17,15 @@ package rx.lang.scala
import rx.concurrency.CurrentThreadScheduler
-package object concurrency {
- /*
- TODO
- rx.concurrency.CurrentThreadScheduler
- rx.concurrency.ExecutorScheduler
- rx.concurrency.ImmediateScheduler
- rx.concurrency.NewThreadScheduler
- rx.concurrency.Schedulers
- rx.concurrency.TestScheduler
+/**
+ * Provides schedulers.
*/
-
- lazy val CurrentThreadScheduler = rx.concurrency.CurrentThreadScheduler.getInstance()
- lazy val NewThreadScheduler = rx.concurrency.NewThreadScheduler.getInstance()
+package object concurrency {
-}
\ No newline at end of file
+ // 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/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala
deleted file mode 100644
index d46bfe1d3b..0000000000
--- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/examples/RxScalaDemo.scala
+++ /dev/null
@@ -1,168 +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.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.Assert._
-import rx.lang.scala.concurrency.NewThreadScheduler
-
-@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)
- o.subscribe(output(_))
- waitFor(o)
- }
-
- @Test def testSwitchOnObservableOfInt() {
- // Correctly rejected with error
- // "Cannot prove that Observable[Int] <:< Observable[Observable[U]]"
- // val o = Observable(1, 2).switch
- }
-
- @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)
- println((before ++ source).toBlockingObservable.toList)
- }
-
- @Test def mergeExample() {
- val slowNumbers = Observable.interval(400 millis).take(5).map("slow " + _)
- val fastNumbers = Observable.interval(200 millis).take(10).map("fast " + _)
- val o = (slowNumbers merge fastNumbers)
- o.subscribe(output(_))
- waitFor(o)
- }
-
- @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(_))
- }
-
- @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() {
- // TODO this does not yet work as expected!
- val o = Observable.interval(100 millis).take(8)
- o.subscribe(
- i => println(s"${i}a (on thread #${Thread.currentThread().getId()})")
- )
- o.subscribe(
- 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(NewThreadScheduler).subscribe(
- i => println(s"${i}a (on thread #${Thread.currentThread().getId()})")
- )
- o.observeOn(NewThreadScheduler).subscribe(
- i => println(s"${i}b (on thread #${Thread.currentThread().getId()})")
- )
- waitFor(o)
- }
-
- def output(s: String): Unit = println(s)
-
- // blocks until obs has completed
- def waitFor[T](obs: Observable[T]): Unit = {
- obs.toBlockingObservable.last
- }
-
-}
diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/internal/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/internal/ImplicitFunctionConversions.scala
deleted file mode 100644
index e5f6e49fc6..0000000000
--- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/internal/ImplicitFunctionConversions.scala
+++ /dev/null
@@ -1,155 +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.internal
-
-
-import java.{lang => jlang}
-import rx.util.functions.Action0
-import rx.util.functions.Action1
-import rx.util.functions.Func0
-import rx.util.functions.Func1
-import rx.util.functions.Func2
-import rx.util.functions.Func3
-import rx.util.functions.Func4
-import java.{lang => jlang}
-import rx.Observer
-import rx.Subscription
-import java.{lang => jlang}
-import scala.language.implicitConversions
-
-/**
- * These function conversions are only used by the ScalaAdapter, users of RxScala don't need them.
- */
-object ImplicitFunctionConversions {
- // code below is copied from
- // https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala
-
- import java.{ lang => jlang }
- import language.implicitConversions
-
- import rx.observables.BlockingObservable
- import rx.util.functions._
- import rx.{Observer, Subscription}
-
- implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) =
- new rx.Observable.OnSubscribeFunc[T] {
- def onSubscribe(obs: Observer[_ >: T]): Subscription = {
- f(obs)
- }
- }
-
- /*implicit def scalaFunction1ToOnSubscribeFunc[T](f: Observer[_ >: T] => Subscription) =
- new rx.Observable.OnSubscribeFunc[T] {
- def onSubscribe(obs: Observer[_ >: T]): Subscription = {
- f(obs)
- }
- }*/
-
- /**
- * Converts a by-name parameter to a Rx Func0
- */
- implicit def scalaByNameParamToFunc0[B](param: => B): Func0[B] =
- new Func0[B]{
- def call(): B = param
- }
-
-
- /**
- * Converts 0-arg function to Rx Action0
- */
- implicit def scalaFunction0ProducingUnitToAction0(f: (() => Unit)): Action0 =
- new Action0 {
- def call(): Unit = f()
- }
-
- /**
- * Converts 1-arg function to Rx Action1
- */
- implicit def scalaFunction1ProducingUnitToAction1[A](f: (A => Unit)): Action1[A] =
- new Action1[A] {
- def call(a: A): Unit = f(a)
- }
-
- /**
- * Converts 1-arg predicate to Rx Func1[A, java.lang.Boolean]
- */
- implicit def scalaBooleanFunction1ToRxBooleanFunc1[A](f: (A => Boolean)): Func1[A, jlang.Boolean] =
- new Func1[A, jlang.Boolean] {
- def call(a: A): jlang.Boolean = f(a).booleanValue
- }
-
- /**
- * Converts 2-arg predicate to Rx Func2[A, B, java.lang.Boolean]
- */
- implicit def scalaBooleanFunction2ToRxBooleanFunc1[A, B](f: ((A, B) => Boolean)): Func2[A, B, jlang.Boolean] =
- new Func2[A, B, jlang.Boolean] {
- def call(a: A, b: B): jlang.Boolean = f(a, b).booleanValue
- }
-
- /**
- * Converts a specific function shape (used in takeWhile) to the equivalent Java types with an Rx Func2
- */
- implicit def convertTakeWhileFuncToRxFunc2[A](f: (A, Int) => Boolean): Func2[A, jlang.Integer, jlang.Boolean] =
- new Func2[A, jlang.Integer, jlang.Boolean] {
- def call(a: A, b: jlang.Integer): jlang.Boolean = f(a, b).booleanValue
- }
-
- /**
- * Converts a function shaped ilke compareTo into the equivalent Rx Func2
- */
- implicit def convertComparisonFuncToRxFunc2[A](f: (A, A) => Int): Func2[A, A, jlang.Integer] =
- new Func2[A, A, jlang.Integer] {
- def call(a1: A, a2: A): jlang.Integer = f(a1, a2).intValue
- }
-
- /*
- * This implicit allows Scala code to use any exception type and still work
- * with invariant Func1 interface
- */
- implicit def exceptionFunction1ToRxExceptionFunc1[A <: Exception, B](f: (A => B)): Func1[Exception, B] =
- new Func1[Exception, B] {
- def call(ex: Exception): B = f(ex.asInstanceOf[A])
- }
-
- /**
- * The following implicits convert functions of different arities into the Rx equivalents
- */
- implicit def scalaFunction0ToRxFunc0[A](f: () => A): Func0[A] =
- new Func0[A] {
- def call(): A = f()
- }
-
- implicit def scalaFunction1ToRxFunc1[A, B](f: (A => B)): Func1[A, B] =
- new Func1[A, B] {
- def call(a: A): B = f(a)
- }
-
- implicit def scalaFunction2ToRxFunc2[A, B, C](f: (A, B) => C): Func2[A, B, C] =
- new Func2[A, B, C] {
- def call(a: A, b: B) = f(a, b)
- }
-
- implicit def scalaFunction3ToRxFunc3[A, B, C, D](f: (A, B, C) => D): Func3[A, B, C, D] =
- new Func3[A, B, C, D] {
- def call(a: A, b: B, c: C) = f(a, b, c)
- }
-
- implicit def scalaFunction4ToRxFunc4[A, B, C, D, E](f: (A, B, C, D) => E): Func4[A, B, C, D, E] =
- new Func4[A, B, C, D, E] {
- def call(a: A, b: B, c: C, d: D) = f(a, b, c, d)
- }
-
-}
\ No newline at end of file
diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala
index 3b3562d652..f9e98efa63 100644
--- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala
@@ -16,46 +16,135 @@
package rx.lang.scala.observables
import scala.collection.JavaConverters._
-import rx.lang.scala.internal.ImplicitFunctionConversions._
+import rx.lang.scala.ImplicitFunctionConversions._
-class BlockingObservable[+T](val asJava: rx.observables.BlockingObservable[_ <: T])
+/**
+ * An Observable that provides blocking operators.
+ *
+ * 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])
extends AnyVal
{
+ /**
+ * Invoke a method on each item emitted by the {@link Observable}; block until the Observable
+ * completes.
+ *
+ * NOTE: This will block even if the Observable is asynchronous.
+ *
+ * This is similar to {@link Observable#subscribe(Observer)}, but it blocks. Because it blocks it does
+ * not need the {@link Observer#onCompleted()} or {@link Observer#onError(Throwable)} methods.
+ *
+ *
+ *
+ * @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)
}
+
+ def withFilter(p: T => Boolean): WithFilter[T] = {
+ new WithFilter[T](p, asJava)
+ }
+
+ // last -> use toIterable.last
+ // lastOrDefault -> use toIterable.lastOption
+ // first -> use toIterable.head
+ // firstOrDefault -> use toIterable.headOption
+ // single(predicate) -> use filter and single
+ // singleOrDefault -> use singleOption
- def last: T = {
- asJava.last() : T // useless ascription because of compiler bug
+ /**
+ * Returns an {@link Iterable} that always returns the item most recently emitted by an {@link Observable}.
+ *
+ *
+ * @param initialValue
+ * the initial value that will be yielded by the {@link Iterable} sequence if the {@link Observable} has not yet emitted an item
+ * @return an {@link Iterable} that on each iteration returns the item that the {@link Observable} has most recently emitted
+ */
+ def mostRecent[U >: T](initialValue: U): Iterable[U] = {
+ val asJavaU = asJava.asInstanceOf[rx.observables.BlockingObservable[U]]
+ asJavaU.mostRecent(initialValue).asScala: Iterable[U] // useless ascription because of compiler bug
}
- // last(Func1 super T, Boolean>)
- // lastOrDefault(T)
- // lastOrDefault(T, Func1 super T, Boolean>)
- // mostRecent(T)
- // next()
+ /**
+ * Returns an {@link Iterable} that blocks until the {@link Observable} emits another item,
+ * then returns that item.
+ *
+ *
+ * @return an {@link Iterable} that blocks upon each iteration until the {@link Observable} emits a new item, whereupon the Iterable returns that item
+ */
+ def next: Iterable[T] = {
+ asJava.next().asScala: Iterable[T] // useless ascription because of compiler bug
+ }
+ /**
+ * If this {@link Observable} completes after emitting a single item, return that item,
+ * otherwise throw an exception.
+ *
+ *
+ * @return the single item emitted by the {@link Observable}
+ */
def single: T = {
- asJava.single() : T // useless ascription because of compiler bug
+ asJava.single(): T // useless ascription because of compiler bug
}
+
+ /**
+ * If this {@link Observable} completes after emitting a single item, return an Option containing
+ * this item, otherwise return {@code None}.
+ */
+ def singleOption: Option[T] = {
+ var size: Int = 0
+ var last: Option[T] = None
+ for (t <- toIterable) {
+ size += 1
+ last = Some(t)
+ }
+ if (size == 1) last else None
+ }
+
+ // TODO toFuture()
+
+ /**
+ * 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
+ }
+
+ /**
+ * 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
+ }
+
+}
+
+// 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[observables] class WithFilter[+T] (p: T => Boolean, asJava: rx.observables.BlockingObservable[_ <: T]) {
+ import rx.lang.scala.ImplicitFunctionConversions._
- // single(Func1 super T, Boolean>)
-
- // def singleOption: Option[T] = { TODO }
- // corresponds to Java's
- // singleOrDefault(T)
+ // there's no map and flatMap here, they're only available on Observable
- // singleOrDefault(BlockingObservable extends T>, boolean, T)
- // singleOrDefault(T, Func1 super T, Boolean>)
- // toFuture()
+ def withFilter(q: T => Boolean) = new WithFilter[T]((x: T) => p(x) && q(x), asJava)
- def toIterable: Iterable[T] = {
- asJava.toIterable().asScala : Iterable[T] // useless ascription because of compiler bug
+ def foreach(f: T => Unit): Unit = {
+ asJava.forEach((e: T) => {
+ if (p(e)) f(e)
+ })
}
- def toList: List[T] = {
- asJava.toIterable().asScala.toList : List[T] // useless ascription because of compiler bug
- }
+}
+
-}
\ No newline at end of file
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
new file mode 100644
index 0000000000..2b43860b53
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala
@@ -0,0 +1,27 @@
+/**
+ * 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
index 6910599783..0809e1fb2b 100644
--- 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
@@ -15,53 +15,34 @@
*/
package rx.lang
+import java.util.concurrent.TimeUnit
+import java.util.Date
-/*
- * This object contains aliases to all types Scala users need to import.
- * Note that:
- * - Scala users cannot use Java's type 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
- * - Type aliases cannot be at top level, they have to be inside an object or class
+/**
+ * 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 {
- type Notification[+T] = rx.Notification[_ <: T]
- object Notification {
- def apply[T](): Notification[T] = new rx.Notification()
- def apply[T](value: T): Notification[T] = new rx.Notification(value)
- def apply[T](t: Throwable): Notification[T] = new rx.Notification(t)
+ /**
+ * 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)
}
-
- type Observer[-T] = rx.Observer[_ >: T]
- type Scheduler = rx.Scheduler
- type Subscription = rx.Subscription
-
}
-
-/*
-
-TODO make aliases for these types because:
-* those which are covariant or contravariant do need an alias to get variance correct
-* the others for consistency
-
-rx.observables.BlockingObservable
-rx.observables.ConnectableObservable
-rx.observables.GroupedObservable
-
-rx.plugins.RxJavaErrorHandler
-rx.plugins.RxJavaObservableExecutionHook
-rx.plugins.RxJavaPlugins
-
-rx.subjects.AsyncSubject
-rx.subjects.BehaviorSubject
-rx.subjects.PublishSubject
-rx.subjects.ReplaySubject
-rx.subjects.Subject
-
-rx.subscriptions.BooleanSubscription
-rx.subscriptions.CompositeSubscription
-rx.subscriptions.Subscriptions
-
-*/
-
diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala
new file mode 100644
index 0000000000..80892b9ac1
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala
@@ -0,0 +1,26 @@
+/**
+ * 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.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/rx/lang/scala/subjects/BehaviorSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala
new file mode 100644
index 0000000000..5b358aba9a
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala
@@ -0,0 +1,30 @@
+/**
+ * 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.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/rx/lang/scala/subjects/PublishSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala
new file mode 100644
index 0000000000..7c06101460
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala
@@ -0,0 +1,27 @@
+/**
+ * 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.subjects
+
+import rx.lang.scala.Subject
+
+object PublishSubject {
+ def apply[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/rx/lang/scala/subjects/ReplaySubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala
new file mode 100644
index 0000000000..f88fb65280
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala
@@ -0,0 +1,30 @@
+/**
+ * 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.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/rx/lang/scala/subjects/Subject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala
new file mode 100644
index 0000000000..08ba9e404c
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/Subject.scala
@@ -0,0 +1,26 @@
+/**
+ * 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
+
+/**
+* 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/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala
new file mode 100644
index 0000000000..7566d5fad8
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala
@@ -0,0 +1,21 @@
+/**
+ * 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
+
+/**
+ * Subjects are Observers and Observables at the same time.
+ */
+package object subjects {}
diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala
new file mode 100644
index 0000000000..be2cb5f392
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala
@@ -0,0 +1,55 @@
+/**
+ * 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
+
+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 = {
+ if(!super.isUnsubscribed()) {
+ 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/rx/lang/scala/subscriptions/CompositeSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala
new file mode 100644
index 0000000000..1fe4a4afa5
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala
@@ -0,0 +1,76 @@
+/**
+ * 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
+
+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/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala
new file mode 100644
index 0000000000..84740870c7
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala
@@ -0,0 +1,69 @@
+/**
+ * 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
+
+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.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/rx/lang/scala/subscriptions/SerialSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala
new file mode 100644
index 0000000000..94b50f93e8
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala
@@ -0,0 +1,63 @@
+/**
+ * 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
+
+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 unsubscribed = new AtomicBoolean(false)
+
+ /**
+ * Checks whether the subscription has been unsubscribed.
+ */
+ def isUnsubscribed: Boolean = unsubscribed.get()
+
+ /**
+ * Unsubscribes this subscription, setting isUnsubscribed to true.
+ */
+ override def unsubscribe(): Unit = { super.unsubscribe(); unsubscribed.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/rx/lang/scala/subscriptions/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/package.scala
new file mode 100644
index 0000000000..c54f83c982
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/package.scala
@@ -0,0 +1,21 @@
+/**
+ * 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 `Subscription`, and specialized versions of it.
+ */
+package object subscriptions {}
diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala
index 6dced72f8e..ed19d849ab 100644
--- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala
+++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/util/package.scala
@@ -15,34 +15,35 @@
*/
package rx.lang.scala
+/**
+ * Provides [[Opening]]s, [[Closing]]s, and [[Timestamped]].
+ */
package object util {
- type Closing = rx.util.Closing
-
- object Closings {
- def create(): Closing = rx.util.Closings.create()
- }
-
- type CompositeException = rx.util.CompositeException
-
- // TODO not sure if we need this in Scala
- object Exceptions {
- def propageate(ex: Throwable) = rx.util.Exceptions.propagate(ex)
- }
-
- // rx.util.OnErrorNotImplementedException TODO what's this?
+ /**
+ * Tagging interface for objects which can open buffers.
+ * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]]
+ */
type Opening = rx.util.Opening
- object Openings {
- def create(): Opening = rx.util.Openings.create()
- }
+ /**
+ * Creates an object which can open buffers.
+ * @see [[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])`]]
+ */
+ type Closing = rx.util.Closing
+ /**
+ * Creates an object which can close buffers.
+ * @see [[Observable `Observable.buffer(Observable[Opening], Opening => Observable[Closing])`]]
+ */
+ def Closing() = rx.util.Closings.create()
+
// rx.util.Range not needed because there's a standard Scala Range
-
- type Timestamped[+T] = rx.util.Timestamped[_ <: T]
- object Timestamped {
- def apply[T](timestampMillis: Long, value: T): Timestamped[T] = {
- new rx.util.Timestamped(timestampMillis, value)
- }
- }
-}
\ No newline at end of file
+
+}
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
new file mode 100644
index 0000000000..f38ac0d521
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala
@@ -0,0 +1,354 @@
+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
" + 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()}`.")
+ }
+
+}
diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala
new file mode 100644
index 0000000000..d96b23fe43
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala
@@ -0,0 +1,88 @@
+package rx.lang.scala
+
+import org.junit.Assert._
+import org.junit.{ Ignore, Test }
+import org.scalatest.junit.JUnitSuite
+
+class ObservableTests 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
+
+ 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/test/scala/rx/lang/scala/RxImplicitsTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/RxImplicitsTests.scala
deleted file mode 100644
index c2252d2ee6..0000000000
--- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/RxImplicitsTests.scala
+++ /dev/null
@@ -1,474 +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 org.scalatest.junit.JUnitSuite
-
-/**
- * This is the test suite for the old Scala adaptor.
- */
-class OldUnitTestSuite extends JUnitSuite {
- import rx.lang.scala.RxImplicits._
-
- import org.junit.{ Before, Test }
- import org.junit.Assert._
- import org.mockito.Matchers.any
- import org.mockito.Mockito._
- import org.mockito.{ MockitoAnnotations, Mock }
- import rx.{ Notification, Observer, Observable, Subscription }
- import rx.Observable.OnSubscribeFunc
- import rx.observables.GroupedObservable
- import rx.subscriptions.Subscriptions
- import collection.mutable.ArrayBuffer
- import collection.JavaConverters._
-
- @Mock private[this]
- val observer: Observer[Any] = null
-
- @Mock private[this]
- val subscription: Subscription = null
-
- val isOdd = (i: Int) => i % 2 == 1
- val isEven = (i: Int) => i % 2 == 0
-
- class OnSubscribeWithException(s: Subscription, values: String*) extends OnSubscribeFunc[String] {
- var t: Thread = null
-
- override def onSubscribe(observer: Observer[_ >: String]): Subscription = {
- println("ObservableWithException subscribed to ...")
- try {
- println("running ObservableWithException thread")
- values.toList.foreach(v => {
- println("ObservableWithException onNext: " + v)
- observer.onNext(v)
- })
- throw new RuntimeException("Forced Failure")
- } catch {
- case ex: Exception => observer.onError(ex)
- }
- s
- }
- }
-
- @Before def before {
- MockitoAnnotations.initMocks(this)
- }
-
- // tests of static methods
- @Test def testSingle {
- assertEquals(1, Observable.from(1).toBlockingObservable.single)
- }
-
- @Test def testSinglePredicate {
- val found = Observable.from(1, 2, 3).toBlockingObservable.single(isEven)
- assertEquals(2, found)
- }
-
- @Test def testSingleOrDefault {
- assertEquals(0, Observable.empty[Int]().toBlockingObservable.singleOrDefault(0))
- assertEquals(1, Observable.from(1).toBlockingObservable.singleOrDefault(0))
- try {
- Observable.from(1, 2, 3).toBlockingObservable.singleOrDefault(0)
- fail("Did not catch any exception, expected IllegalStateException")
- } catch {
- case ex: IllegalStateException => println("Caught expected IllegalStateException")
- case ex: Throwable => fail("Caught unexpected exception " + ex.getCause + ", expected IllegalStateException")
- }
- }
-
- @Test def testSingleOrDefaultPredicate {
- assertEquals(2, Observable.from(1, 2, 3).toBlockingObservable.singleOrDefault(0, isEven))
- assertEquals(0, Observable.from(1, 3).toBlockingObservable.singleOrDefault(0, isEven))
- try {
- Observable.from(1, 2, 3).toBlockingObservable.singleOrDefault(0, isOdd)
- fail("Did not catch any exception, expected IllegalStateException")
- } catch {
- case ex: IllegalStateException => println("Caught expected IllegalStateException")
- case ex: Throwable => fail("Caught unexpected exception " + ex.getCause + ", expected IllegalStateException")
- }
- }
-
- @Test def testCreateFromOnSubscribeFunc {
- val created = Observable.create((o: Observer[_ >: Integer]) => Subscriptions.empty)
- //no assertions on subscription, just testing the implicit
- }
-
- @Test def testFromJavaInterop {
- val observable = Observable.from(List(1, 2, 3).asJava)
- assertSubscribeReceives(observable)(1, 2, 3)
- }
-
- @Test def testSubscribe {
- val observable = Observable.from("1", "2", "3")
- assertSubscribeReceives(observable)("1", "2", "3")
- }
-
- //should not compile - adapted from https://gist.github.com/jmhofer/5195589
- /*@Test def testSubscribeOnInt() {
- val observable = Observable.from("1", "2", "3")
- observable.subscribe((arg: Int) => {
- println("testSubscribe: arg = " + arg)
- })
- }*/
-
- @Test def testDefer {
- val lazyObservableFactory = () => Observable.from(1, 2)
- val observable = Observable.defer(lazyObservableFactory)
- assertSubscribeReceives(observable)(1, 2)
- }
-
- @Test def testJust {
- val observable = Observable.just("foo")
- assertSubscribeReceives(observable)("foo")
- }
-
- @Test def testMerge {
- val observable1 = Observable.from(1, 2, 3)
- val observable2 = Observable.from(4, 5, 6)
- val merged = Observable.merge(observable1, observable2)
- assertSubscribeReceives(merged)(1, 2, 3, 4, 5, 6)
- }
-
- @Test def testFlattenMerge {
- val observable = Observable.from(Observable.from(1, 2, 3))
- val merged = Observable.merge[Int](observable)
- assertSubscribeReceives(merged)(1, 2, 3)
- }
-
- @Test def testSequenceMerge {
- val observable1 = Observable.from(1, 2, 3)
- val observable2 = Observable.from(4, 5, 6)
- val merged = Observable.merge(observable1, observable2)
- assertSubscribeReceives(merged)(1, 2, 3, 4, 5, 6)
- }
-
- @Test def testConcat {
- val observable1 = Observable.from(1, 2, 3)
- val observable2 = Observable.from(4, 5, 6)
- val concatenated = Observable.concat(observable1, observable2)
- assertSubscribeReceives(concatenated)(1, 2, 3, 4, 5, 6)
- }
-
- @Test def testSynchronize {
- val observable = Observable.from(1, 2, 3)
- val synchronized = Observable.synchronize(observable)
- assertSubscribeReceives(synchronized)(1, 2, 3)
- }
-
- @Test def testZip2() {
- val colors: Observable[String] = Observable.from("red", "green", "blue")
- val names: Observable[String] = Observable.from("lion-o", "cheetara", "panthro")
-
- case class Character(color: String, name: String)
-
- val cheetara = Character("green", "cheetara")
- val panthro = Character("blue", "panthro")
- val characters = Observable.zip[String, String, Character](colors, names, Character.apply _)
- assertSubscribeReceives(characters)(cheetara, panthro)
- }
-
- @Test def testZip3() {
- val numbers = Observable.from(1, 2, 3)
- val colors = Observable.from("red", "green", "blue")
- val names = Observable.from("lion-o", "cheetara", "panthro")
-
- case class Character(id: Int, color: String, name: String)
-
- val liono = Character(1, "red", "lion-o")
- val cheetara = Character(2, "green", "cheetara")
- val panthro = Character(3, "blue", "panthro")
-
- val characters = Observable.zip[Int, String, String, Character](numbers, colors, names, Character.apply _)
- assertSubscribeReceives(characters)(liono, cheetara, panthro)
- }
-
- @Test def testZip4() {
- val numbers = Observable.from(1, 2, 3)
- val colors = Observable.from("red", "green", "blue")
- val names = Observable.from("lion-o", "cheetara", "panthro")
- val isLeader = Observable.from(true, false, false)
-
- case class Character(id: Int, color: String, name: String, isLeader: Boolean)
-
- val liono = Character(1, "red", "lion-o", true)
- val cheetara = Character(2, "green", "cheetara", false)
- val panthro = Character(3, "blue", "panthro", false)
-
- val characters = Observable.zip[Int, String, String, Boolean, Character](numbers, colors, names, isLeader, Character.apply _)
- assertSubscribeReceives(characters)(liono, cheetara, panthro)
- }
-
- //tests of instance methods
-
- // missing tests for : takeUntil, groupBy, next, mostRecent
-
- @Test def testFilter {
- val numbers = Observable.from(1, 2, 3, 4, 5, 6, 7, 8, 9)
- val observable = numbers.filter(isEven)
- assertSubscribeReceives(observable)(2, 4, 6, 8)
- }
-
- @Test def testLast {
- val observable = Observable.from(1, 2, 3, 4)
- assertEquals(4, observable.toBlockingObservable.last)
- }
-
- @Test def testLastPredicate {
- val observable = Observable.from(1, 2, 3, 4)
- assertEquals(3, observable.toBlockingObservable.last(isOdd))
- }
-
- @Test def testLastOrDefault {
- val observable = Observable.from(1, 2, 3, 4)
- assertEquals(4, observable.toBlockingObservable.lastOrDefault(5))
- assertEquals(5, Observable.empty[Int]().toBlockingObservable.lastOrDefault(5))
- }
-
- @Test def testLastOrDefaultPredicate {
- val observable = Observable.from(1, 2, 3, 4)
- assertEquals(3, observable.toBlockingObservable.lastOrDefault(5, isOdd))
- assertEquals(5, Observable.empty[Int]().toBlockingObservable.lastOrDefault(5, isOdd))
- }
-
- @Test def testMap {
- val numbers = Observable.from(1, 2, 3, 4, 5, 6, 7, 8, 9)
- val mappedNumbers = ArrayBuffer.empty[Int]
- val mapped: Observable[Int] = numbers map ((x: Int) => x * x)
- mapped.subscribe((squareVal: Int) => {
- mappedNumbers.append(squareVal)
- })
- assertEquals(List(1, 4, 9, 16, 25, 36, 49, 64, 81), mappedNumbers.toList)
- }
-
- @Test def testMapMany {
- val numbers = Observable.from(1, 2, 3, 4)
- val f = (i: Int) => Observable.from(List(i, -i).asJava)
- val mappedNumbers = ArrayBuffer.empty[Int]
- numbers.mapMany(f).subscribe((i: Int) => {
- mappedNumbers.append(i)
- })
- assertEquals(List(1, -1, 2, -2, 3, -3, 4, -4), mappedNumbers.toList)
- }
-
- @Test def testMaterialize {
- val observable = Observable.from(1, 2, 3, 4)
- val expectedNotifications: List[Notification[Int]] =
- ((1.to(4).map(i => new Notification(i))) :+ new Notification()).toList
- val actualNotifications: ArrayBuffer[Notification[Int]] = ArrayBuffer.empty
- observable.materialize.subscribe((n: Notification[Int]) => {
- actualNotifications.append(n)
- })
- assertEquals(expectedNotifications, actualNotifications.toList)
- }
-
- @Test def testDematerialize {
- val notifications: List[Notification[Int]] =
- ((1.to(4).map(i => new Notification(i))) :+ new Notification()).toList
- val observableNotifications: Observable[Notification[Int]] =
- Observable.from(notifications.asJava)
- val observable: Observable[Int] =
- observableNotifications.dematerialize()
- assertSubscribeReceives(observable)(1, 2, 3, 4)
- }
-
- @Test def testOnErrorResumeNextObservableNoError {
- val observable = Observable.from(1, 2, 3, 4)
- val resumeObservable = Observable.from(5, 6, 7, 8)
- val observableWithErrorHandler = observable.onErrorResumeNext(resumeObservable)
- assertSubscribeReceives(observableWithErrorHandler)(1, 2, 3, 4)
- }
-
- @Test def testOnErrorResumeNextObservableErrorOccurs {
- val observable = Observable.create(new OnSubscribeWithException(subscription, "foo", "bar"))
- val resumeObservable = Observable.from("a", "b", "c", "d")
- val observableWithErrorHandler = observable.onErrorResumeNext(resumeObservable)
- observableWithErrorHandler.subscribe(observer.asInstanceOf[Observer[String]])
-
- List("foo", "bar", "a", "b", "c", "d").foreach(t => verify(observer, times(1)).onNext(t))
- verify(observer, never()).onError(any(classOf[Exception]))
- verify(observer, times(1)).onCompleted()
- }
-
- @Test def testOnErrorResumeNextFuncNoError {
- val observable = Observable.from(1, 2, 3, 4)
- val resumeFunc = (ex: Throwable) => Observable.from(5, 6, 7, 8)
- val observableWithErrorHandler = observable.onErrorResumeNext(resumeFunc)
- assertSubscribeReceives(observableWithErrorHandler)(1, 2, 3, 4)
- }
-
- @Test def testOnErrorResumeNextFuncErrorOccurs {
- val observable = Observable.create(new OnSubscribeWithException(subscription, "foo", "bar"))
- val resumeFunc = (ex: Throwable) => Observable.from("a", "b", "c", "d")
- val observableWithErrorHandler = observable.onErrorResumeNext(resumeFunc)
- observableWithErrorHandler.subscribe(observer.asInstanceOf[Observer[String]])
-
- List("foo", "bar", "a", "b", "c", "d").foreach(t => verify(observer, times(1)).onNext(t))
- verify(observer, never()).onError(any(classOf[Exception]))
- verify(observer, times(1)).onCompleted()
- }
-
- @Test def testOnErrorReturnFuncNoError {
- val observable = Observable.from(1, 2, 3, 4)
- val returnFunc = (ex: Throwable) => 87
- val observableWithErrorHandler = observable.onErrorReturn(returnFunc)
- assertSubscribeReceives(observableWithErrorHandler)(1, 2, 3, 4)
- }
-
- @Test def testOnErrorReturnFuncErrorOccurs {
- val observable = Observable.create(new OnSubscribeWithException(subscription, "foo", "bar"))
- val returnFunc = (ex: Throwable) => "baz"
- val observableWithErrorHandler = observable.onErrorReturn(returnFunc)
- observableWithErrorHandler.subscribe(observer.asInstanceOf[Observer[String]])
-
- List("foo", "bar", "baz").foreach(t => verify(observer, times(1)).onNext(t))
- verify(observer, never()).onError(any(classOf[Exception]))
- verify(observer, times(1)).onCompleted()
- }
-
- @Test def testReduce {
- val observable = Observable.from(1, 2, 3, 4)
- assertEquals(10, observable.reduce((a: Int, b: Int) => a + b).toBlockingObservable.single)
- }
-
- @Test def testSkip {
- val observable = Observable.from(1, 2, 3, 4)
- val skipped = observable.skip(2)
- assertSubscribeReceives(skipped)(3, 4)
- }
-
- @Test def testTake {
- val observable = Observable.from(1, 2, 3, 4, 5)
- val took = observable.take(2)
- assertSubscribeReceives(took)(1, 2)
- }
-
- @Test def testTakeWhile {
- val observable = Observable.from(1, 3, 5, 6, 7, 9, 11)
- val took = observable.takeWhile(isOdd)
- assertSubscribeReceives(took)(1, 3, 5)
- }
-
- @Test def testTakeWhileWithIndex {
- val observable = Observable.from(1, 3, 5, 7, 9, 11, 12, 13, 15, 17)
- val took = observable.takeWhileWithIndex((i: Int, idx: Int) => isOdd(i) && idx < 8)
- assertSubscribeReceives(took)(1, 3, 5, 7, 9, 11)
- }
-
- @Test def testTakeLast {
- val observable = Observable.from(1, 2, 3, 4, 5, 6, 7, 8, 9)
- val tookLast = observable.takeLast(3)
- assertSubscribeReceives(tookLast)(7, 8, 9)
- }
-
- @Test def testToList {
- val observable = Observable.from(1, 2, 3, 4)
- val toList = observable.toList
- assertSubscribeReceives(toList)(List(1, 2, 3, 4).asJava)
- }
-
- @Test def testToSortedList {
- val observable = Observable.from(1, 3, 4, 2)
- val toSortedList = observable.toSortedList
- assertSubscribeReceives(toSortedList)(List(1, 2, 3, 4).asJava)
- }
-
- @Test def testToArbitrarySortedList {
- val observable = Observable.from("a", "aaa", "aaaa", "aa")
- val sortByLength = (s1: String, s2: String) => s1.length.compareTo(s2.length)
- val toSortedList = observable.toSortedList(sortByLength)
- assertSubscribeReceives(toSortedList)(List("a", "aa", "aaa", "aaaa").asJava)
- }
-
- @Test def testToIterable {
- val observable = Observable.from(1, 2)
- val it = observable.toBlockingObservable.toIterable.iterator
- assertTrue(it.hasNext)
- assertEquals(1, it.next)
- assertTrue(it.hasNext)
- assertEquals(2, it.next)
- assertFalse(it.hasNext)
- }
-
- @Test def testStartWith {
- val observable = Observable.from(1, 2, 3, 4)
- val newStart = observable.startWith(-1, 0)
- assertSubscribeReceives(newStart)(-1, 0, 1, 2, 3, 4)
- }
-
- @Test def testOneLineForComprehension {
- val mappedObservable = for {
- i: Int <- Observable.from(1, 2, 3, 4)
- } yield i + 1
- assertSubscribeReceives(mappedObservable)(2, 3, 4, 5)
- assertFalse(mappedObservable.isInstanceOf[ScalaObservable[_]])
- }
-
- @Test def testSimpleMultiLineForComprehension {
- val flatMappedObservable = for {
- i: Int <- Observable.from(1, 2, 3, 4)
- j: Int <- Observable.from(1, 10, 100, 1000)
- } yield i + j
- assertSubscribeReceives(flatMappedObservable)(2, 12, 103, 1004)
- assertFalse(flatMappedObservable.isInstanceOf[ScalaObservable[_]])
- }
-
- @Test def testMultiLineForComprehension {
- val doubler = (i: Int) => Observable.from(i, i)
- val flatMappedObservable = for {
- i: Int <- Observable.from(1, 2, 3, 4)
- j: Int <- doubler(i)
- } yield j
- //can't use assertSubscribeReceives since each number comes in 2x
- flatMappedObservable.subscribe(observer.asInstanceOf[Observer[Int]])
- List(1, 2, 3, 4).foreach(i => verify(observer, times(2)).onNext(i))
- verify(observer, never()).onError(any(classOf[Exception]))
- verify(observer, times(1)).onCompleted()
- assertFalse(flatMappedObservable.isInstanceOf[ScalaObservable[_]])
- }
-
- @Test def testFilterInForComprehension {
- val doubler = (i: Int) => Observable.from(i, i)
- val filteredObservable: Observable[Int] = for {
- i: Int <- Observable.from(1, 2, 3, 4)
- j: Int <- doubler(i) if isOdd(i)
- } yield j
- //can't use assertSubscribeReceives since each number comes in 2x
- filteredObservable.subscribe(observer.asInstanceOf[Observer[Int]])
- List(1, 3).foreach(i => verify(observer, times(2)).onNext(i))
- verify(observer, never()).onError(any(classOf[Exception]))
- verify(observer, times(1)).onCompleted()
- assertFalse(filteredObservable.isInstanceOf[ScalaObservable[_]])
- }
-
- @Test def testForEachForComprehension {
- val doubler = (i: Int) => Observable.from(i, i)
- val intBuffer = ArrayBuffer.empty[Int]
- val forEachComprehension = for {
- i: Int <- Observable.from(1, 2, 3, 4)
- j: Int <- doubler(i) if isEven(i)
- } {
- intBuffer.append(j)
- }
- assertEquals(List(2, 2, 4, 4), intBuffer.toList)
- }
-
- private def assertSubscribeReceives[T](o: Observable[T])(values: T*) = {
- o.subscribe(observer.asInstanceOf[Observer[T]])
- values.toList.foreach(t => verify(observer, times(1)).onNext(t))
- verify(observer, never()).onError(any(classOf[Exception]))
- verify(observer, times(1)).onCompleted()
- }
-}
diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala
new file mode 100644
index 0000000000..4309967c0a
--- /dev/null
+++ b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/subscriptions/SubscriptionTests.scala
@@ -0,0 +1,118 @@
+package rx.lang.scala.subscriptions
+
+import org.junit.Assert._
+import org.junit.Test
+import org.scalatest.junit.JUnitSuite
+
+import rx.lang.scala.Subscription
+
+class SubscriptionTests extends JUnitSuite {
+
+ @Test
+ def anonymousSubscriptionCreate() {
+ val subscription = Subscription{}
+ assertNotNull(subscription)
+ }
+
+ @Test
+ def anonymousSubscriptionDispose() {
+ var unsubscribed = false
+ val subscription = Subscription{ unsubscribed = true }
+ assertFalse(unsubscribed)
+ subscription.unsubscribe()
+ assertTrue(unsubscribed)
+ }
+
+ @Test
+ def emptySubscription() {
+ val subscription = Subscription()
+ subscription.unsubscribe()
+ }
+
+ @Test
+ def booleanSubscription() {
+ val subscription = BooleanSubscription()
+ assertFalse(subscription.isUnsubscribed)
+ subscription.unsubscribe()
+ assertTrue(subscription.isUnsubscribed)
+ subscription.unsubscribe()
+ 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()
+
+ assertFalse(composite.isUnsubscribed)
+
+ composite += s0
+ composite += s1
+
+ composite.unsubscribe()
+
+ assertTrue(composite.isUnsubscribed)
+ assertTrue(s0.isUnsubscribed)
+ assertTrue(u0)
+ assertTrue(u1)
+
+ val s2 = BooleanSubscription()
+ assertFalse(s2.isUnsubscribed)
+ composite += s2
+ assertTrue(s2.isUnsubscribed)
+
+ }
+
+ @Test
+ def compositeSubscriptionRemove() {
+
+ val s0 = BooleanSubscription()
+ val composite = CompositeSubscription()
+
+ composite += s0
+ assertFalse(s0.isUnsubscribed)
+ composite -= s0
+ assertTrue(s0.isUnsubscribed)
+
+ composite.unsubscribe()
+
+ assertTrue(composite.isUnsubscribed)
+ }
+
+ @Test
+ def multiAssignmentSubscriptionAdd() {
+
+ val s0 = BooleanSubscription()
+ val s1 = BooleanSubscription()
+ val multiple = MultipleAssignmentSubscription()
+
+ assertFalse(multiple.isUnsubscribed)
+
+ multiple.subscription = s0
+ assertEquals(s0.asJavaSubscription, multiple.subscription.asJavaSubscription)
+
+ multiple.subscription = s1
+ assertEquals(s1.asJavaSubscription, multiple.subscription.asJavaSubscription)
+
+ assertFalse(s0.isUnsubscribed)
+ assertFalse(s1.isUnsubscribed)
+
+ multiple.unsubscribe()
+
+ assertTrue(multiple.isUnsubscribed)
+ assertFalse(s0.isUnsubscribed)
+ assertTrue(s1.isUnsubscribed)
+
+ val s2 = BooleanSubscription()
+ assertFalse(s2.isUnsubscribed)
+ multiple.subscription = s2
+ assertTrue(s2.isUnsubscribed)
+ }
+
+}
diff --git a/rxjava-contrib/rxjava-android/build.gradle b/rxjava-contrib/rxjava-android/build.gradle
index f3b7745c81..144d3cd68a 100644
--- a/rxjava-contrib/rxjava-android/build.gradle
+++ b/rxjava-contrib/rxjava-android/build.gradle
@@ -2,10 +2,13 @@ apply plugin: 'osgi'
dependencies {
compile project(':rxjava-core')
+ provided 'com.google.android:android:4.0.1.2'
+ provided 'com.google.android:support-v4:r7'
+
+ // testing
provided 'junit:junit-dep:4.10'
provided 'org.mockito:mockito-core:1.8.5'
provided 'org.robolectric:robolectric:2.1.1'
- provided 'com.google.android:android:4.0.1.2'
}
javadoc {
diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/AndroidSchedulers.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/AndroidSchedulers.java
index 36a8154d16..0b238b1644 100644
--- a/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/AndroidSchedulers.java
+++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/AndroidSchedulers.java
@@ -1,3 +1,18 @@
+/**
+ * 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.android.concurrency;
import android.os.Handler;
diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java
index cd1af987ae..ae01d17c1a 100644
--- a/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java
+++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java
@@ -1,3 +1,18 @@
+/**
+ * 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.android.concurrency;
import android.os.Handler;
diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java
new file mode 100644
index 0000000000..c70ba970c3
--- /dev/null
+++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java
@@ -0,0 +1,40 @@
+/**
+ * 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.android.observables;
+
+import rx.Observable;
+import rx.operators.OperationObserveFromAndroidComponent;
+
+import android.app.Activity;
+import android.app.Fragment;
+
+public final class AndroidObservable {
+
+ private AndroidObservable() {}
+
+ public static