Skip to content

Commit e6a2951

Browse files
committed
Followed the iterator contract
1 parent c4e3a6c commit e6a2951

File tree

1 file changed

+113
-19
lines changed

1 file changed

+113
-19
lines changed

rxjava-core/src/main/java/rx/operators/OperationNext.java

Lines changed: 113 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717

1818
import static org.junit.Assert.assertEquals;
1919
import static org.junit.Assert.assertFalse;
20-
import static org.junit.Assert.assertNull;
2120
import static org.junit.Assert.assertTrue;
2221
import static org.junit.Assert.fail;
2322

2423
import java.util.Iterator;
24+
import java.util.NoSuchElementException;
2525
import java.util.concurrent.ArrayBlockingQueue;
2626
import java.util.concurrent.BlockingQueue;
2727
import java.util.concurrent.CountDownLatch;
@@ -70,6 +70,7 @@ private static class NextIterator<T> implements Iterator<T> {
7070
private final NextObserver<? extends T> observer;
7171
private T next;
7272
private boolean hasNext = true;
73+
private boolean isNextConsumed = true;
7374

7475
private NextIterator(NextObserver<? extends T> observer) {
7576
this.observer = observer;
@@ -80,23 +81,34 @@ public boolean hasNext() {
8081
// Since an iterator should not be used in different thread,
8182
// so we do not need any synchronization.
8283
if(hasNext == false) {
84+
// the iterator has reached the end.
8385
return false;
8486
}
87+
if(isNextConsumed == false) {
88+
// next has not been used yet.
89+
return true;
90+
}
91+
return moveToNext();
92+
}
93+
94+
private boolean moveToNext() {
8595
try {
8696
Notification<? extends T> nextNotification = observer.takeNext();
8797
if(nextNotification.isOnNext()) {
98+
isNextConsumed = false;
8899
next = nextNotification.getValue();
89100
return true;
90101
}
91102
// If an observable is completed or fails,
92-
// next always return null and hasNext always return false.
93-
next = null;
103+
// hasNext() always return false.
94104
hasNext = false;
95105
if(nextNotification.isOnCompleted()) {
96106
return false;
97107
}
98-
// onError
99-
throw Exceptions.propagate(nextNotification.getThrowable());
108+
if(nextNotification.isOnError()) {
109+
throw Exceptions.propagate(nextNotification.getThrowable());
110+
}
111+
throw new IllegalStateException("Should not reach here");
100112
} catch (InterruptedException e) {
101113
Thread.currentThread().interrupt();
102114
throw Exceptions.propagate(e);
@@ -105,7 +117,13 @@ public boolean hasNext() {
105117

106118
@Override
107119
public T next() {
108-
return next;
120+
if(hasNext()) {
121+
isNextConsumed = true;
122+
return next;
123+
}
124+
else {
125+
throw new NoSuchElementException("No more elements");
126+
}
109127
}
110128

111129
@Override
@@ -197,11 +215,21 @@ public void testNext() {
197215

198216
obs.onCompleted();
199217
assertFalse(it.hasNext());
200-
assertNull(it.next());
218+
try {
219+
it.next();
220+
fail("At the end of an iterator should throw a NoSuchElementException");
221+
}
222+
catch(NoSuchElementException e){
223+
}
201224

202-
// If the observable is completed, hasNext always returns false and next always returns null.
225+
// If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException.
203226
assertFalse(it.hasNext());
204-
assertNull(it.next());
227+
try {
228+
it.next();
229+
fail("At the end of an iterator should throw a NoSuchElementException");
230+
}
231+
catch(NoSuchElementException e){
232+
}
205233
}
206234

207235
@Test
@@ -221,9 +249,14 @@ public void testNextWithError() {
221249
// successful
222250
}
223251

224-
// After the observable fails, hasNext always returns false and next always returns null.
252+
// After the observable fails, hasNext always returns false and next always throw a NoSuchElementException.
225253
assertFalse(it.hasNext());
226-
assertNull(it.next());
254+
try {
255+
it.next();
256+
fail("At the end of an iterator should throw a NoSuchElementException");
257+
}
258+
catch(NoSuchElementException e){
259+
}
227260
}
228261

229262
@Test
@@ -232,11 +265,21 @@ public void testNextWithEmpty() {
232265
Iterator<String> it = next(obs).iterator();
233266

234267
assertFalse(it.hasNext());
235-
assertNull(it.next());
236-
237-
// If the observable is completed, hasNext always returns false and next always returns null.
268+
try {
269+
it.next();
270+
fail("At the end of an iterator should throw a NoSuchElementException");
271+
}
272+
catch(NoSuchElementException e){
273+
}
274+
275+
// If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException.
238276
assertFalse(it.hasNext());
239-
assertNull(it.next());
277+
try {
278+
it.next();
279+
fail("At the end of an iterator should throw a NoSuchElementException");
280+
}
281+
catch(NoSuchElementException e){
282+
}
240283
}
241284

242285
@Test
@@ -253,9 +296,14 @@ public void testOnError() throws Throwable {
253296
// successful
254297
}
255298

256-
// After the observable fails, hasNext always returns false and next always returns null.
299+
// After the observable fails, hasNext always returns false and next always throw a NoSuchElementException.
257300
assertFalse(it.hasNext());
258-
assertNull(it.next());
301+
try {
302+
it.next();
303+
fail("At the end of an iterator should throw a NoSuchElementException");
304+
}
305+
catch(NoSuchElementException e){
306+
}
259307
}
260308

261309
@Test
@@ -273,9 +321,53 @@ public void testOnErrorInNewThread() {
273321
// successful
274322
}
275323

276-
// After the observable fails, hasNext always returns false and next always returns null.
324+
// After the observable fails, hasNext always returns false and next always throw a NoSuchElementException.
277325
assertFalse(it.hasNext());
278-
assertNull(it.next());
326+
try {
327+
it.next();
328+
fail("At the end of an iterator should throw a NoSuchElementException");
329+
}
330+
catch(NoSuchElementException e){
331+
}
332+
}
333+
334+
@Test
335+
public void testNextWithOnlyUsingNextMethod() {
336+
Subject<String, String> obs = PublishSubject.create();
337+
Iterator<String> it = next(obs).iterator();
338+
fireOnNextInNewThread(obs, "one");
339+
assertEquals("one", it.next());
340+
341+
fireOnNextInNewThread(obs, "two");
342+
assertEquals("two", it.next());
343+
344+
obs.onCompleted();
345+
try {
346+
it.next();
347+
fail("At the end of an iterator should throw a NoSuchElementException");
348+
}
349+
catch(NoSuchElementException e){
350+
}
351+
}
352+
353+
@Test
354+
public void testNextWithCallingHasNextMultipleTimes() {
355+
Subject<String, String> obs = PublishSubject.create();
356+
Iterator<String> it = next(obs).iterator();
357+
fireOnNextInNewThread(obs, "one");
358+
assertTrue(it.hasNext());
359+
assertTrue(it.hasNext());
360+
assertTrue(it.hasNext());
361+
assertTrue(it.hasNext());
362+
assertEquals("one", it.next());
363+
364+
obs.onCompleted();
365+
try {
366+
it.next();
367+
fail("At the end of an iterator should throw a NoSuchElementException");
368+
}
369+
catch(NoSuchElementException e){
370+
}
279371
}
280372

281373
@SuppressWarnings("serial")
@@ -342,6 +434,8 @@ public void run() {
342434
assertTrue("expected that c [" + c + "] is higher than or equal to " + COUNT, c >= COUNT);
343435

344436
assertTrue(it.hasNext());
437+
int d = it.next();
438+
assertTrue(d > c);
345439

346440
// shut down the thread
347441
running.set(false);

0 commit comments

Comments
 (0)