16
16
package rx .lang .scala .observables
17
17
18
18
import scala .collection .JavaConverters ._
19
+ import scala .concurrent .{Future , Promise }
19
20
import rx .lang .scala .ImplicitFunctionConversions ._
21
+ import rx .lang .scala .Observable
22
+ import rx .observables .{BlockingObservable => JBlockingObservable }
20
23
21
24
22
25
/**
23
26
* An Observable that provides blocking operators.
24
27
*
25
- * You can obtain a BlockingObservable from an Observable using [[rx.lang.scala.Observable.toBlockingObservable ]]
28
+ * You can obtain a BlockingObservable from an Observable using [[rx.lang.scala.Observable.toBlocking ]]
26
29
*/
27
- // constructor is private because users should use Observable.toBlockingObservable
28
- class BlockingObservable [+ T ] private [scala] (val asJava : rx.observables. BlockingObservable [_ <: T ])
29
- extends AnyVal
30
+ // constructor is private because users should use Observable.toBlocking
31
+ class BlockingObservable [+ T ] private [scala] (val o : Observable [ T ])
32
+ extends AnyVal
30
33
{
31
-
34
+ // This is def because "field definition is not allowed in value class"
35
+ private def asJava : JBlockingObservable [_ <: T ] = o.asJavaObservable.toBlocking
32
36
/**
33
37
* Invoke a method on each item emitted by the {@link Observable}; block until the Observable
34
38
* completes.
@@ -69,6 +73,31 @@ class BlockingObservable[+T] private[scala] (val asJava: rx.observables.Blocking
69
73
asJava.last : T
70
74
}
71
75
76
+ /**
77
+ * Returns an `Option` with the last item emitted by the source Observable,
78
+ * or `None` if the source Observable completes without emitting any items.
79
+ *
80
+ * @return an `Option` with the last item emitted by the source Observable,
81
+ * or `None` if the source Observable is empty
82
+ */
83
+ def lastOption : Option [T ] = {
84
+ o.lastOption.toBlocking.single
85
+ }
86
+
87
+ /**
88
+ * Returns the last item emitted by the source Observable, or a default item
89
+ * if the source Observable completes without emitting any items.
90
+ *
91
+ * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/lastOrDefault.png">
92
+ *
93
+ * @param default the default item to emit if the source Observable is empty.
94
+ * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything.
95
+ * @return the last item emitted by the source Observable, or a default item if the source Observable is empty
96
+ */
97
+ def lastOrElse [U >: T ](default : => U ): U = {
98
+ lastOption getOrElse default
99
+ }
100
+
72
101
/**
73
102
* Returns the first item emitted by a specified [[Observable ]], or
74
103
* `NoSuchElementException` if source contains no elements.
@@ -96,12 +125,29 @@ class BlockingObservable[+T] private[scala] (val asJava: rx.observables.Blocking
96
125
*/
97
126
def head : T = first
98
127
99
- // last -> use toIterable.last
100
- // lastOrDefault -> use toIterable.lastOption
101
- // first -> use toIterable.head
102
- // firstOrDefault -> use toIterable.headOption
103
- // single(predicate) -> use filter and single
104
- // singleOrDefault -> use singleOption
128
+ /**
129
+ * Returns an `Option` with the very first item emitted by the source Observable,
130
+ * or `None` if the source Observable is empty.
131
+ *
132
+ * @return an `Option` with the very first item from the source,
133
+ * or `None` if the source Observable completes without emitting any item.
134
+ */
135
+ def headOption : Option [T ] = {
136
+ o.headOption.toBlocking.single
137
+ }
138
+
139
+ /**
140
+ * Returns the very first item emitted by the source Observable, or a default value if the source Observable is empty.
141
+ *
142
+ * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/firstOrDefault.png">
143
+ *
144
+ * @param default The default value to emit if the source Observable doesn't emit anything.
145
+ * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything.
146
+ * @return the very first item from the source, or a default value if the source Observable completes without emitting any item.
147
+ */
148
+ def headOrElse [U >: T ](default : => U ): U = {
149
+ headOption getOrElse default
150
+ }
105
151
106
152
/**
107
153
* Returns an {@link Iterable} that always returns the item most recently emitted by an {@link Observable}.
@@ -130,32 +176,48 @@ class BlockingObservable[+T] private[scala] (val asJava: rx.observables.Blocking
130
176
}
131
177
132
178
/**
133
- * If this {@link Observable} completes after emitting a single item, return that item,
134
- * otherwise throw an exception .
135
- * <p>
136
- * <img width="640" src="https://github.com/Netflix/RxJava/wiki/ images/rx-operators/B. single.png">
179
+ * If the source Observable completes after emitting a single item, return that item. If the source Observable
180
+ * emits more than one item or no items, notify of an `IllegalArgumentException` or `NoSuchElementException` respectively .
181
+ *
182
+ * <img width="640" src="https://raw. github.com/wiki/ Netflix/RxJava/images/rx-operators/single.png">
137
183
*
138
- * @return the single item emitted by the {@link Observable}
184
+ * @return an Observable that emits the single item emitted by the source Observable
185
+ * @throws IllegalArgumentException if the source emits more than one item
186
+ * @throws NoSuchElementException if the source emits no items
139
187
*/
140
188
def single : T = {
141
189
asJava.single(): T // useless ascription because of compiler bug
142
190
}
143
191
144
192
/**
145
- * If this {@link Observable} completes after emitting a single item, return an Option containing
146
- * this item, otherwise return {@code None}.
193
+ * If the source Observable completes after emitting a single item, return an `Option` with that item;
194
+ * if the source Observable is empty, return `None`. If the source Observable emits more than one item,
195
+ * throw an `IllegalArgumentException`.
196
+ *
197
+ * @return an `Option` with the single item emitted by the source Observable, or
198
+ * `None` if the source Observable is empty
199
+ * @throws IllegalArgumentException if the source Observable emits more than one item
147
200
*/
148
201
def singleOption : Option [T ] = {
149
- var size : Int = 0
150
- var last : Option [T ] = None
151
- for (t <- toIterable) {
152
- size += 1
153
- last = Some (t)
154
- }
155
- if (size == 1 ) last else None
202
+ o.singleOption.toBlocking.single
156
203
}
157
204
158
- // TODO toFuture()
205
+ /**
206
+ * If the source Observable completes after emitting a single item, return that item;
207
+ * if the source Observable is empty, return a default item. If the source Observable
208
+ * emits more than one item, throw an `IllegalArgumentException`.
209
+ *
210
+ * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/singleOrDefault.png">
211
+ *
212
+ * @param default a default value to emit if the source Observable emits no item.
213
+ * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything.
214
+ * @return the single item emitted by the source Observable, or a default item if
215
+ * the source Observable is empty
216
+ * @throws IllegalArgumentException if the source Observable emits more than one item
217
+ */
218
+ def singleOrElse [U >: T ](default : => U ): U = {
219
+ singleOption getOrElse default
220
+ }
159
221
160
222
/**
161
223
* Returns an {@link Iterator} that iterates over all items emitted by this {@link Observable}.
@@ -171,6 +233,38 @@ class BlockingObservable[+T] private[scala] (val asJava: rx.observables.Blocking
171
233
asJava.toIterable.asScala.toList: List [T ] // useless ascription because of compiler bug
172
234
}
173
235
236
+ /**
237
+ * Returns an `Iterable` that returns the latest item emitted by this `BlockingObservable`,
238
+ * waiting if necessary for one to become available.
239
+ *
240
+ * If this `BlockingObservable` produces items faster than `Iterator.next` takes them,
241
+ * `onNext` events might be skipped, but `onError` or `onCompleted` events are not.
242
+ *
243
+ * Note also that an `onNext` directly followed by `onCompleted` might hide the `onNext` event.
244
+ *
245
+ * @return an `Iterable` that always returns the latest item emitted by this `BlockingObservable`
246
+ */
247
+ def latest : Iterable [T ] = {
248
+ asJava.latest.asScala: Iterable [T ] // useless ascription because of compiler bug
249
+ }
250
+
251
+ /**
252
+ * Returns a `Future` representing the single value emitted by this `BlockingObservable`.
253
+ *
254
+ * The returned `Future` will be completed with an `IllegalArgumentException` if the `BlockingObservable`
255
+ * emits more than one item. And it will be completed with an `NoSuchElementException` if the `BlockingObservable`
256
+ * is empty. Use `Observable.toSeq.toBlocking.toFuture` if you are not sure about the size of `BlockingObservable`
257
+ * and do not want to handle these `Exception`s.
258
+ *
259
+ * <img width="640" height="395" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/B.toFuture.png">
260
+ *
261
+ * @return a `Future` that expects a single item to be emitted by this `BlockingObservable`.
262
+ */
263
+ def toFuture : Future [T ] = {
264
+ val p = Promise [T ]()
265
+ o.single.subscribe(t => p.success(t), e => p.failure(e))
266
+ p.future
267
+ }
174
268
}
175
269
176
270
// Cannot yet have inner class because of this error message:
0 commit comments