Files
HMCL/HMCLAPI/src/main/java/rx/operators/OperationNext.java
T
2016-01-01 11:03:09 +08:00

144 lines
4.0 KiB
Java
Executable File

/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Notification;
import rx.Observable;
import rx.Observer;
import rx.util.Exceptions;
/**
* Samples the next value (blocking without buffering) from in an observable
* sequence.
*/
public final class OperationNext {
public static <T> Iterable<T> next(final Observable<T> items) {
NextObserver<T> nextObserver = new NextObserver<>();
final NextIterator<T> nextIterator = new NextIterator<>(nextObserver);
items.materialize().subscribe(nextObserver);
return () -> nextIterator;
}
private static class NextIterator<T> implements Iterator<T> {
private final NextObserver<T> observer;
private NextIterator(NextObserver<T> observer) {
this.observer = observer;
}
@Override
public boolean hasNext() {
return !observer.isCompleted(false);
}
@Override
public T next() {
if (observer.isCompleted(true))
throw new IllegalStateException("Observable is completed");
observer.await();
try {
return observer.takeNext();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw Exceptions.propagate(e);
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("Read only iterator");
}
}
private static class NextObserver<T> implements Observer<Notification<T>> {
private final BlockingQueue<Notification<T>> buf = new ArrayBlockingQueue<>(1);
private final AtomicBoolean waiting = new AtomicBoolean(false);
@Override
public void onCompleted() {
// ignore
}
@Override
public void onError(Exception e) {
// ignore
}
@Override
public void onNext(Notification<T> args) {
if (waiting.getAndSet(false) || !args.isOnNext()) {
Notification<T> toOffer = args;
while (!buf.offer(toOffer)) {
Notification<T> concurrentItem = buf.poll();
// in case if we won race condition with onComplete/onError method
if (!concurrentItem.isOnNext())
toOffer = concurrentItem;
}
}
}
public void await() {
waiting.set(true);
}
public boolean isCompleted(boolean rethrowExceptionIfExists) {
Notification<T> lastItem = buf.peek();
if (lastItem == null)
return false;
if (lastItem.isOnError())
if (rethrowExceptionIfExists)
throw Exceptions.propagate(lastItem.getException());
else
return true;
return lastItem.isOnCompleted();
}
public T takeNext() throws InterruptedException {
Notification<T> next = buf.take();
if (next.isOnError())
throw Exceptions.propagate(next.getException());
if (next.isOnCompleted())
throw new IllegalStateException("Observable is completed");
return next.getValue();
}
}
}