25
25
26
26
import org .junit .Before ;
27
27
import org .junit .Test ;
28
+ import org .mockito .InOrder ;
28
29
import org .mockito .Mock ;
29
30
import org .mockito .MockitoAnnotations ;
30
31
33
34
import rx .Observer ;
34
35
import rx .Subscription ;
35
36
import rx .util .functions .Func1 ;
37
+ import rx .util .functions .Func2 ;
36
38
37
39
/**
38
40
* Applies a function of your choosing to every item emitted by an Observable, and returns this
@@ -56,8 +58,42 @@ public final class OperationMap {
56
58
* the type of the output sequence.
57
59
* @return a sequence that is the result of applying the transformation function to each item in the input sequence.
58
60
*/
59
- public static <T , R > OnSubscribeFunc <R > map (Observable <? extends T > sequence , Func1 <? super T , ? extends R > func ) {
60
- return new MapObservable <T , R >(sequence , func );
61
+ public static <T , R > OnSubscribeFunc <R > map (final Observable <? extends T > sequence , final Func1 <? super T , ? extends R > func ) {
62
+ return new OnSubscribeFunc <R >() {
63
+ @ Override
64
+ public Subscription onSubscribe (Observer <? super R > observer ) {
65
+ return new MapObservable <T , R >(sequence , new Func2 <T , Integer , R >() {
66
+ @ Override
67
+ public R call (T value , @ SuppressWarnings ("unused" ) Integer unused ) {
68
+ return func .call (value );
69
+ }
70
+ }).onSubscribe (observer );
71
+ }
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Accepts a sequence and a transformation function. Returns a sequence that is the result of
77
+ * applying the transformation function to each item in the sequence.
78
+ *
79
+ * @param sequence
80
+ * the input sequence.
81
+ * @param func
82
+ * a function to apply to each item in the sequence. The function gets the index of the emitted item
83
+ * as additional parameter.
84
+ * @param <T>
85
+ * the type of the input sequence.
86
+ * @param <R>
87
+ * the type of the output sequence.
88
+ * @return a sequence that is the result of applying the transformation function to each item in the input sequence.
89
+ */
90
+ public static <T , R > OnSubscribeFunc <R > mapWithIndex (final Observable <? extends T > sequence , final Func2 <? super T , Integer , ? extends R > func ) {
91
+ return new OnSubscribeFunc <R >() {
92
+ @ Override
93
+ public Subscription onSubscribe (Observer <? super R > observer ) {
94
+ return new MapObservable <T , R >(sequence , func ).onSubscribe (observer );
95
+ }
96
+ };
61
97
}
62
98
63
99
/**
@@ -89,56 +125,50 @@ public static <T, R> OnSubscribeFunc<R> mapMany(Observable<? extends T> sequence
89
125
* the type of the output sequence.
90
126
*/
91
127
private static class MapObservable <T , R > implements OnSubscribeFunc <R > {
92
- public MapObservable (Observable <? extends T > sequence , Func1 <? super T , ? extends R > func ) {
128
+ public MapObservable (Observable <? extends T > sequence , Func2 <? super T , Integer , ? extends R > func ) {
93
129
this .sequence = sequence ;
94
130
this .func = func ;
95
131
}
96
132
97
- private Observable <? extends T > sequence ;
98
-
99
- private Func1 <? super T , ? extends R > func ;
100
-
101
- public Subscription onSubscribe (Observer <? super R > observer ) {
102
- return sequence .subscribe (new MapObserver <T , R >(observer , func ));
103
- }
104
- }
105
-
106
- /**
107
- * An observer that applies a transformation function to each item and forwards the result to an inner observer.
108
- *
109
- * @param <T>
110
- * the type of the observer items.
111
- * @param <R>
112
- * the type of the inner observer items.
113
- */
114
- private static class MapObserver <T , R > implements Observer <T > {
115
- public MapObserver (Observer <? super R > observer , Func1 <? super T , ? extends R > func ) {
116
- this .observer = observer ;
117
- this .func = func ;
118
- }
119
-
120
- Observer <? super R > observer ;
121
-
122
- Func1 <? super T , ? extends R > func ;
133
+ private final Observable <? extends T > sequence ;
134
+ private final Func2 <? super T , Integer , ? extends R > func ;
135
+ private int index ;
123
136
124
- public void onNext (T value ) {
125
- // let the exception be thrown if func fails as a SafeObserver wrapping this will handle it
126
- observer .onNext (func .call (value ));
127
- }
137
+ @ Override
138
+ public Subscription onSubscribe (final Observer <? super R > observer ) {
139
+ return sequence .subscribe (new Observer <T >() {
140
+ @ Override
141
+ public void onNext (T value ) {
142
+ observer .onNext (func .call (value , index ));
143
+ index ++;
144
+ }
128
145
129
- public void onError (Throwable ex ) {
130
- observer .onError (ex );
131
- }
146
+ @ Override
147
+ public void onError (Throwable ex ) {
148
+ observer .onError (ex );
149
+ }
132
150
133
- public void onCompleted () {
134
- observer .onCompleted ();
151
+ @ Override
152
+ public void onCompleted () {
153
+ observer .onCompleted ();
154
+ }
155
+ });
135
156
}
136
157
}
137
158
138
159
public static class UnitTest {
139
160
@ Mock
140
161
Observer <String > stringObserver ;
141
-
162
+ @ Mock
163
+ Observer <String > stringObserver2 ;
164
+
165
+ final static Func2 <String , Integer , String > APPEND_INDEX = new Func2 <String , Integer , String >() {
166
+ @ Override
167
+ public String call (String value , Integer index ) {
168
+ return value + index ;
169
+ }
170
+ };
171
+
142
172
@ Before
143
173
public void before () {
144
174
MockitoAnnotations .initMocks (this );
@@ -164,9 +194,42 @@ public String call(Map<String, String> map) {
164
194
verify (stringObserver , times (1 )).onNext ("OneFirst" );
165
195
verify (stringObserver , times (1 )).onNext ("TwoFirst" );
166
196
verify (stringObserver , times (1 )).onCompleted ();
197
+ }
167
198
199
+ @ Test
200
+ public void testMapWithIndex () {
201
+ Observable <String > w = Observable .from ("a" , "b" , "c" );
202
+ Observable <String > m = Observable .create (mapWithIndex (w , APPEND_INDEX ));
203
+ m .subscribe (stringObserver );
204
+ InOrder inOrder = inOrder (stringObserver );
205
+ inOrder .verify (stringObserver , times (1 )).onNext ("a0" );
206
+ inOrder .verify (stringObserver , times (1 )).onNext ("b1" );
207
+ inOrder .verify (stringObserver , times (1 )).onNext ("c2" );
208
+ inOrder .verify (stringObserver , times (1 )).onCompleted ();
209
+ verify (stringObserver , never ()).onError (any (Throwable .class ));
168
210
}
211
+
212
+ @ Test
213
+ public void testMapWithIndexAndMultipleSubscribers () {
214
+ Observable <String > w = Observable .from ("a" , "b" , "c" );
215
+ Observable <String > m = Observable .create (mapWithIndex (w , APPEND_INDEX ));
216
+ m .subscribe (stringObserver );
217
+ m .subscribe (stringObserver2 );
218
+ InOrder inOrder = inOrder (stringObserver );
219
+ inOrder .verify (stringObserver , times (1 )).onNext ("a0" );
220
+ inOrder .verify (stringObserver , times (1 )).onNext ("b1" );
221
+ inOrder .verify (stringObserver , times (1 )).onNext ("c2" );
222
+ inOrder .verify (stringObserver , times (1 )).onCompleted ();
223
+ verify (stringObserver , never ()).onError (any (Throwable .class ));
169
224
225
+ InOrder inOrder2 = inOrder (stringObserver2 );
226
+ inOrder2 .verify (stringObserver2 , times (1 )).onNext ("a0" );
227
+ inOrder2 .verify (stringObserver2 , times (1 )).onNext ("b1" );
228
+ inOrder2 .verify (stringObserver2 , times (1 )).onNext ("c2" );
229
+ inOrder2 .verify (stringObserver2 , times (1 )).onCompleted ();
230
+ verify (stringObserver2 , never ()).onError (any (Throwable .class ));
231
+ }
232
+
170
233
@ Test
171
234
public void testMapMany () {
172
235
/* simulate a top-level async call which returns IDs */
@@ -246,12 +309,34 @@ public String call(Map<String, String> map) {
246
309
247
310
}
248
311
312
+ @ Test
313
+ public void testMapWithError () {
314
+ Observable <String > w = Observable .from ("one" , "fail" , "two" , "three" , "fail" );
315
+ Observable <String > m = Observable .create (map (w , new Func1 <String , String >() {
316
+ @ Override
317
+ public String call (String s ) {
318
+ if ("fail" .equals (s )) {
319
+ throw new RuntimeException ("Forced Failure" );
320
+ }
321
+ return s ;
322
+ }
323
+ }));
324
+
325
+ m .subscribe (stringObserver );
326
+ verify (stringObserver , times (1 )).onNext ("one" );
327
+ verify (stringObserver , never ()).onNext ("two" );
328
+ verify (stringObserver , never ()).onNext ("three" );
329
+ verify (stringObserver , never ()).onCompleted ();
330
+ verify (stringObserver , times (1 )).onError (any (Throwable .class ));
331
+ }
332
+
249
333
@ Test
250
334
public void testMapWithSynchronousObservableContainingError () {
251
335
Observable <String > w = Observable .from ("one" , "fail" , "two" , "three" , "fail" );
252
336
final AtomicInteger c1 = new AtomicInteger ();
253
337
final AtomicInteger c2 = new AtomicInteger ();
254
338
Observable <String > m = Observable .create (map (w , new Func1 <String , String >() {
339
+ @ Override
255
340
public String call (String s ) {
256
341
if ("fail" .equals (s ))
257
342
throw new RuntimeException ("Forced Failure" );
@@ -260,6 +345,7 @@ public String call(String s) {
260
345
return s ;
261
346
}
262
347
})).map (new Func1 <String , String >() {
348
+ @ Override
263
349
public String call (String s ) {
264
350
System .out .println ("SecondMapper:" + s );
265
351
c2 .incrementAndGet ();
@@ -280,7 +366,7 @@ public String call(String s) {
280
366
assertEquals (1 , c2 .get ());
281
367
}
282
368
283
- private Map <String , String > getMap (String prefix ) {
369
+ private static Map <String , String > getMap (String prefix ) {
284
370
Map <String , String > m = new HashMap <String , String >();
285
371
m .put ("firstName" , prefix + "First" );
286
372
m .put ("lastName" , prefix + "Last" );
0 commit comments