15
15
*/
16
16
package rx .operators ;
17
17
18
- import static org .mockito .Matchers .*;
19
- import static org .mockito .Mockito .*;
18
+ import static org .mockito .Matchers .any ;
19
+ import static org .mockito .Mockito .inOrder ;
20
+ import static org .mockito .Mockito .mock ;
21
+ import static org .mockito .Mockito .never ;
22
+ import static org .mockito .Mockito .times ;
23
+ import static org .mockito .Mockito .verify ;
20
24
21
- import java .util .Iterator ;
22
- import java .util .concurrent .LinkedBlockingDeque ;
25
+ import java .util .Deque ;
26
+ import java .util .LinkedList ;
27
+ import java .util .concurrent .locks .ReentrantLock ;
23
28
24
29
import org .junit .Test ;
25
30
import org .mockito .InOrder ;
26
31
27
32
import rx .Observable ;
28
33
import rx .Observable .OnSubscribeFunc ;
29
- import rx .subscriptions .Subscriptions ;
30
34
import rx .Observer ;
31
35
import rx .Subscription ;
32
36
@@ -60,45 +64,36 @@ private static class TakeLast<T> implements OnSubscribeFunc<T> {
60
64
}
61
65
62
66
public Subscription onSubscribe (Observer <? super T > observer ) {
63
- if (count == 0 ) {
64
- items .subscribe (new Observer <T >() {
65
-
66
- @ Override
67
- public void onCompleted () {
68
- }
69
-
70
- @ Override
71
- public void onError (Throwable e ) {
72
- }
73
-
74
- @ Override
75
- public void onNext (T args ) {
76
- }
77
-
78
- }).unsubscribe ();
79
- observer .onCompleted ();
80
- return Subscriptions .empty ();
67
+ if (count < 0 ) {
68
+ throw new IndexOutOfBoundsException (
69
+ "count could not be negative" );
81
70
}
82
-
83
71
return subscription .wrap (items .subscribe (new ItemObserver (observer )));
84
72
}
85
73
86
74
private class ItemObserver implements Observer <T > {
87
75
88
- private LinkedBlockingDeque <T > deque = new LinkedBlockingDeque <T >(count );
76
+ /**
77
+ * Store the last count elements until now.
78
+ */
79
+ private Deque <T > deque = new LinkedList <T >();
89
80
private final Observer <? super T > observer ;
81
+ private final ReentrantLock lock = new ReentrantLock ();
90
82
91
83
public ItemObserver (Observer <? super T > observer ) {
92
84
this .observer = observer ;
93
85
}
94
86
95
87
@ Override
96
88
public void onCompleted () {
97
- Iterator <T > reverse = deque .descendingIterator ();
98
- while (reverse .hasNext ()) {
99
- observer .onNext (reverse .next ());
89
+ try {
90
+ for (T value : deque ) {
91
+ observer .onNext (value );
92
+ }
93
+ observer .onCompleted ();
94
+ } catch (Throwable e ) {
95
+ observer .onError (e );
100
96
}
101
- observer .onCompleted ();
102
97
}
103
98
104
99
@ Override
@@ -107,9 +102,27 @@ public void onError(Throwable e) {
107
102
}
108
103
109
104
@ Override
110
- public void onNext (T args ) {
111
- while (!deque .offerFirst (args )) {
112
- deque .removeLast ();
105
+ public void onNext (T value ) {
106
+ if (count == 0 ) {
107
+ // If count == 0, we do not need to put value into deque and
108
+ // remove it at once. We can ignore the value directly.
109
+ return ;
110
+ }
111
+ lock .lock ();
112
+ try {
113
+ deque .offerLast (value );
114
+ if (deque .size () > count ) {
115
+ // Now deque has count + 1 elements, so the first
116
+ // element in the deque definitely does not belong
117
+ // to the last count elements of the source
118
+ // sequence. We can drop it now.
119
+ deque .removeFirst ();
120
+ }
121
+ } catch (Throwable e ) {
122
+ observer .onError (e );
123
+ subscription .unsubscribe ();
124
+ } finally {
125
+ lock .unlock ();
113
126
}
114
127
}
115
128
@@ -174,6 +187,35 @@ public void testTakeLastWithZeroCount() {
174
187
verify (aObserver , times (1 )).onCompleted ();
175
188
}
176
189
190
+ @ Test
191
+ public void testTakeLastWithNull () {
192
+ Observable <String > w = Observable .from ("one" , null , "three" );
193
+ Observable <String > take = Observable .create (takeLast (w , 2 ));
194
+
195
+ @ SuppressWarnings ("unchecked" )
196
+ Observer <String > aObserver = mock (Observer .class );
197
+ take .subscribe (aObserver );
198
+ verify (aObserver , never ()).onNext ("one" );
199
+ verify (aObserver , times (1 )).onNext (null );
200
+ verify (aObserver , times (1 )).onNext ("three" );
201
+ verify (aObserver , never ()).onError (any (Throwable .class ));
202
+ verify (aObserver , times (1 )).onCompleted ();
203
+ }
204
+
205
+ @ Test
206
+ public void testTakeLastWithNegativeCount () {
207
+ Observable <String > w = Observable .from ("one" );
208
+ Observable <String > take = Observable .create (takeLast (w , -1 ));
209
+
210
+ @ SuppressWarnings ("unchecked" )
211
+ Observer <String > aObserver = mock (Observer .class );
212
+ take .subscribe (aObserver );
213
+ verify (aObserver , never ()).onNext ("one" );
214
+ verify (aObserver , times (1 )).onError (
215
+ any (IndexOutOfBoundsException .class ));
216
+ verify (aObserver , never ()).onCompleted ();
217
+ }
218
+
177
219
}
178
220
179
221
}
0 commit comments