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 ;
@@ -59,25 +64,36 @@ private static class TakeLast<T> implements OnSubscribeFunc<T> {
59
64
}
60
65
61
66
public Subscription onSubscribe (Observer <? super T > observer ) {
67
+ if (count < 0 ) {
68
+ throw new IndexOutOfBoundsException (
69
+ "count could not be negative" );
70
+ }
62
71
return subscription .wrap (items .subscribe (new ItemObserver (observer )));
63
72
}
64
73
65
74
private class ItemObserver implements Observer <T > {
66
75
67
- 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 >();
68
80
private final Observer <? super T > observer ;
81
+ private final ReentrantLock lock = new ReentrantLock ();
69
82
70
83
public ItemObserver (Observer <? super T > observer ) {
71
84
this .observer = observer ;
72
85
}
73
86
74
87
@ Override
75
88
public void onCompleted () {
76
- Iterator <T > reverse = deque .descendingIterator ();
77
- while (reverse .hasNext ()) {
78
- 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 );
79
96
}
80
- observer .onCompleted ();
81
97
}
82
98
83
99
@ Override
@@ -86,9 +102,27 @@ public void onError(Throwable e) {
86
102
}
87
103
88
104
@ Override
89
- public void onNext (T args ) {
90
- while (!deque .offerFirst (args )) {
91
- 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 ();
92
126
}
93
127
}
94
128
@@ -140,6 +174,48 @@ public void testTakeLast2() {
140
174
verify (aObserver , times (1 )).onCompleted ();
141
175
}
142
176
177
+ @ Test
178
+ public void testTakeLastWithZeroCount () {
179
+ Observable <String > w = Observable .from ("one" );
180
+ Observable <String > take = Observable .create (takeLast (w , 0 ));
181
+
182
+ @ SuppressWarnings ("unchecked" )
183
+ Observer <String > aObserver = mock (Observer .class );
184
+ take .subscribe (aObserver );
185
+ verify (aObserver , never ()).onNext ("one" );
186
+ verify (aObserver , never ()).onError (any (Throwable .class ));
187
+ verify (aObserver , times (1 )).onCompleted ();
188
+ }
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
+
143
219
}
144
220
145
221
}
0 commit comments