Change the license to GPL v3 to be compatible with Apache License 2.0.
This commit is contained in:
161
HMCLAPI/src/main/java/rx/Notification.java
Normal file
161
HMCLAPI/src/main/java/rx/Notification.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* An object representing a notification sent to an {@link Observable}.
|
||||
*
|
||||
* For the Microsoft Rx equivalent see: http://msdn.microsoft.com/en-us/library/hh229462(v=vs.103).aspx
|
||||
*/
|
||||
public class Notification<T> {
|
||||
|
||||
private final Kind kind;
|
||||
private final Exception exception;
|
||||
private final T value;
|
||||
|
||||
/**
|
||||
* A constructor used to represent an onNext notification.
|
||||
*
|
||||
* @param value
|
||||
* The data passed to the onNext method.
|
||||
*/
|
||||
public Notification(T value) {
|
||||
this.value = value;
|
||||
this.exception = null;
|
||||
this.kind = Kind.OnNext;
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor used to represent an onError notification.
|
||||
*
|
||||
* @param exception
|
||||
* The exception passed to the onError notification.
|
||||
*/
|
||||
public Notification(Exception exception) {
|
||||
this.exception = exception;
|
||||
this.value = null;
|
||||
this.kind = Kind.OnError;
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor used to represent an onCompleted notification.
|
||||
*/
|
||||
public Notification() {
|
||||
this.exception = null;
|
||||
this.value = null;
|
||||
this.kind = Kind.OnCompleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the exception associated with an onError notification.
|
||||
*
|
||||
* @return The exception associated with an onError notification.
|
||||
*/
|
||||
public Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the data associated with an onNext notification.
|
||||
*
|
||||
* @return The data associated with an onNext notification.
|
||||
*/
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a value indicating whether this notification has a value.
|
||||
*
|
||||
* @return a value indicating whether this notification has a value.
|
||||
*/
|
||||
public boolean hasValue() {
|
||||
return isOnNext() && value != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a value indicating whether this notification has an exception.
|
||||
*
|
||||
* @return a value indicating whether this notification has an exception.
|
||||
*/
|
||||
public boolean hasException() {
|
||||
return isOnError() && exception != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The kind of notification: OnNext, OnError, OnCompleted
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Kind getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public boolean isOnError() {
|
||||
return getKind() == Kind.OnError;
|
||||
}
|
||||
|
||||
public boolean isOnCompleted() {
|
||||
return getKind() == Kind.OnCompleted;
|
||||
}
|
||||
|
||||
public boolean isOnNext() {
|
||||
return getKind() == Kind.OnNext;
|
||||
}
|
||||
|
||||
public static enum Kind {
|
||||
OnNext, OnError, OnCompleted
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder("[").append(super.toString()).append(" ").append(getKind());
|
||||
if (hasValue())
|
||||
str.append(" ").append(getValue());
|
||||
if (hasException())
|
||||
str.append(" ").append(getException().getMessage());
|
||||
str.append("]");
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = getKind().hashCode();
|
||||
if (hasValue())
|
||||
hash = hash * 31 + getValue().hashCode();
|
||||
if (hasException())
|
||||
hash = hash * 31 + getException().hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj.getClass() != getClass())
|
||||
return false;
|
||||
Notification<?> notification = (Notification<?>) obj;
|
||||
if (notification.getKind() != getKind())
|
||||
return false;
|
||||
if (hasValue() && !getValue().equals(notification.getValue()))
|
||||
return false;
|
||||
if (hasException() && !getException().equals(notification.getException()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
3273
HMCLAPI/src/main/java/rx/Observable.java
Normal file
3273
HMCLAPI/src/main/java/rx/Observable.java
Normal file
File diff suppressed because it is too large
Load Diff
57
HMCLAPI/src/main/java/rx/Observer.java
Normal file
57
HMCLAPI/src/main/java/rx/Observer.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Provides a mechanism for receiving push-based notifications.
|
||||
* <p>
|
||||
* After an Observer calls an {@link Observable}'s <code>Observable.subscribe</code> method, the {@link Observable} calls the Observer's <code>onNext</code> method to provide notifications. A
|
||||
* well-behaved {@link Observable} will
|
||||
* call an Observer's <code>onCompleted</code> closure exactly once or the Observer's <code>onError</code> closure exactly once.
|
||||
* <p>
|
||||
* For more information see the <a href="https://github.com/Netflix/RxJava/wiki/Observable">RxJava Wiki</a>
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface Observer<T> {
|
||||
|
||||
/**
|
||||
* Notifies the Observer that the {@link Observable} has finished sending push-based notifications.
|
||||
* <p>
|
||||
* The {@link Observable} will not call this closure if it calls <code>onError</code>.
|
||||
*/
|
||||
public void onCompleted();
|
||||
|
||||
/**
|
||||
* Notifies the Observer that the {@link Observable} has experienced an error condition.
|
||||
* <p>
|
||||
* If the {@link Observable} calls this closure, it will not thereafter call <code>onNext</code> or <code>onCompleted</code>.
|
||||
*
|
||||
* @param e
|
||||
*/
|
||||
public void onError(Exception e);
|
||||
|
||||
/**
|
||||
* Provides the Observer with new data.
|
||||
* <p>
|
||||
* The {@link Observable} calls this closure 1 or more times, unless it calls <code>onError</code> in which case this closure may never be called.
|
||||
* <p>
|
||||
* The {@link Observable} will not call this closure again after it calls either <code>onCompleted</code> or <code>onError</code>.
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public void onNext(T args);
|
||||
}
|
||||
69
HMCLAPI/src/main/java/rx/Scheduler.java
Normal file
69
HMCLAPI/src/main/java/rx/Scheduler.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.util.functions.Action0;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
* Represents an object that schedules units of work.
|
||||
*/
|
||||
public interface Scheduler {
|
||||
|
||||
/**
|
||||
* Schedules a cancelable action to be executed.
|
||||
*
|
||||
* @param action
|
||||
* action
|
||||
* @return a subscription to be able to unsubscribe from action.
|
||||
*/
|
||||
Subscription schedule(Func0<Subscription> action);
|
||||
|
||||
/**
|
||||
* Schedules an action to be executed.
|
||||
*
|
||||
* @param action
|
||||
* action
|
||||
* @return a subscription to be able to unsubscribe from action.
|
||||
*/
|
||||
Subscription schedule(Action0 action);
|
||||
|
||||
/**
|
||||
* Schedules an action to be executed in dueTime.
|
||||
*
|
||||
* @param action
|
||||
* action
|
||||
* @return a subscription to be able to unsubscribe from action.
|
||||
*/
|
||||
Subscription schedule(Action0 action, long dueTime, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Schedules a cancelable action to be executed in dueTime.
|
||||
*
|
||||
* @param action
|
||||
* action
|
||||
* @return a subscription to be able to unsubscribe from action.
|
||||
*/
|
||||
Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Returns the scheduler's notion of current time.
|
||||
*/
|
||||
long now();
|
||||
|
||||
}
|
||||
34
HMCLAPI/src/main/java/rx/Subscription.java
Normal file
34
HMCLAPI/src/main/java/rx/Subscription.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import rx.subscriptions.Subscriptions;
|
||||
|
||||
/**
|
||||
* Subscription returns from {@link Observable#subscribe(Observer)} to allow unsubscribing.
|
||||
* <p>
|
||||
* See utilities in {@link Subscriptions} and implementations in the {@link rx.subscriptions} package.
|
||||
*/
|
||||
public interface Subscription {
|
||||
|
||||
/**
|
||||
* Stop receiving notifications on the {@link Observer} that was registered when this Subscription was received.
|
||||
* <p>
|
||||
* This allows unregistering an {@link Observer} before it has finished receiving all events (ie. before onCompleted is called).
|
||||
*/
|
||||
public void unsubscribe();
|
||||
|
||||
}
|
||||
53
HMCLAPI/src/main/java/rx/concurrency/AbstractScheduler.java
Normal file
53
HMCLAPI/src/main/java/rx/concurrency/AbstractScheduler.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.concurrency;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Scheduler;
|
||||
import rx.Subscription;
|
||||
import rx.subscriptions.Subscriptions;
|
||||
import rx.util.functions.Action0;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/* package */abstract class AbstractScheduler implements Scheduler {
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Action0 action) {
|
||||
return schedule(asFunc0(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Action0 action, long dueTime, TimeUnit unit) {
|
||||
return schedule(asFunc0(action), dueTime, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long now() {
|
||||
return System.nanoTime();
|
||||
}
|
||||
|
||||
private static Func0<Subscription> asFunc0(final Action0 action) {
|
||||
return new Func0<Subscription>() {
|
||||
@Override
|
||||
public Subscription call() {
|
||||
action.call();
|
||||
return Subscriptions.empty();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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.concurrency;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
* Schedules work on the current thread but does not execute immediately. Work is put in a queue and executed after the current unit of work is completed.
|
||||
*/
|
||||
public class CurrentThreadScheduler extends AbstractScheduler {
|
||||
private static final CurrentThreadScheduler INSTANCE = new CurrentThreadScheduler();
|
||||
|
||||
public static CurrentThreadScheduler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static final ThreadLocal<Queue<DiscardableAction>> QUEUE = new ThreadLocal<>();
|
||||
|
||||
private CurrentThreadScheduler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action) {
|
||||
DiscardableAction discardableAction = new DiscardableAction(action);
|
||||
enqueue(discardableAction);
|
||||
return discardableAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
|
||||
return schedule(new SleepingAction(action, this, dueTime, unit));
|
||||
}
|
||||
|
||||
private void enqueue(DiscardableAction action) {
|
||||
Queue<DiscardableAction> queue = QUEUE.get();
|
||||
boolean exec = queue == null;
|
||||
|
||||
if (exec) {
|
||||
queue = new LinkedList<>();
|
||||
QUEUE.set(queue);
|
||||
}
|
||||
|
||||
queue.add(action);
|
||||
|
||||
if (exec) {
|
||||
while (!queue.isEmpty()) {
|
||||
queue.poll().call();
|
||||
}
|
||||
|
||||
QUEUE.set(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
HMCLAPI/src/main/java/rx/concurrency/DiscardableAction.java
Normal file
52
HMCLAPI/src/main/java/rx/concurrency/DiscardableAction.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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.concurrency;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
* Combines standard {@link Subscription#unsubscribe()} functionality with ability to skip execution if an unsubscribe occurs before the {@link #call()} method is invoked.
|
||||
*/
|
||||
/* package */class DiscardableAction implements Func0<Subscription>, Subscription {
|
||||
private final Func0<Subscription> underlying;
|
||||
|
||||
private final AtomicObservableSubscription wrapper = new AtomicObservableSubscription();
|
||||
private final AtomicBoolean ready = new AtomicBoolean(true);
|
||||
|
||||
public DiscardableAction(Func0<Subscription> underlying) {
|
||||
this.underlying = underlying;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call() {
|
||||
if (ready.compareAndSet(true, false)) {
|
||||
Subscription subscription = underlying.call();
|
||||
wrapper.wrap(subscription);
|
||||
return subscription;
|
||||
}
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
ready.set(false);
|
||||
wrapper.unsubscribe();
|
||||
}
|
||||
}
|
||||
126
HMCLAPI/src/main/java/rx/concurrency/ExecutorScheduler.java
Normal file
126
HMCLAPI/src/main/java/rx/concurrency/ExecutorScheduler.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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.concurrency;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import rx.Scheduler;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
* A {@link Scheduler} implementation that uses an {@link Executor} or {@link ScheduledExecutorService} implementation.
|
||||
* <p>
|
||||
* Note that if an {@link Executor} implementation is used instead of {@link ScheduledExecutorService} then a system-wide Timer will be used to handle delayed events.
|
||||
*/
|
||||
public class ExecutorScheduler extends AbstractScheduler {
|
||||
private final Executor executor;
|
||||
|
||||
/**
|
||||
* Setup a ScheduledExecutorService that we can use if someone provides an Executor instead of ScheduledExecutorService.
|
||||
*/
|
||||
private final static ScheduledExecutorService SYSTEM_SCHEDULED_EXECUTOR;
|
||||
static {
|
||||
int count = Runtime.getRuntime().availableProcessors();
|
||||
if (count > 8) {
|
||||
count = count / 2;
|
||||
}
|
||||
// we don't need more than 8 to handle just scheduling and doing no work
|
||||
if (count > 8) {
|
||||
count = 8;
|
||||
}
|
||||
SYSTEM_SCHEDULED_EXECUTOR = Executors.newScheduledThreadPool(count, new ThreadFactory() {
|
||||
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r, "RxScheduledExecutorPool-" + counter.incrementAndGet());
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public ExecutorScheduler(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public ExecutorScheduler(ScheduledExecutorService executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
|
||||
final DiscardableAction discardableAction = new DiscardableAction(action);
|
||||
|
||||
if (executor instanceof ScheduledExecutorService) {
|
||||
((ScheduledExecutorService) executor).schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
discardableAction.call();
|
||||
}
|
||||
}, dueTime, unit);
|
||||
} else {
|
||||
if (dueTime == 0) {
|
||||
// no delay so put on the thread-pool right now
|
||||
return (schedule(action));
|
||||
} else {
|
||||
// there is a delay and this isn't a ScheduledExecutorService so we'll use a system-wide ScheduledExecutorService
|
||||
// to handle the scheduling and once it's ready then execute on this Executor
|
||||
SYSTEM_SCHEDULED_EXECUTOR.schedule(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// now execute on the real Executor
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
discardableAction.call();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}, dueTime, unit);
|
||||
}
|
||||
}
|
||||
return discardableAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action) {
|
||||
final DiscardableAction discardableAction = new DiscardableAction(action);
|
||||
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
discardableAction.call();
|
||||
}
|
||||
});
|
||||
|
||||
return discardableAction;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
46
HMCLAPI/src/main/java/rx/concurrency/ImmediateScheduler.java
Normal file
46
HMCLAPI/src/main/java/rx/concurrency/ImmediateScheduler.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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.concurrency;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
* Executes work immediately on the current thread.
|
||||
*/
|
||||
public final class ImmediateScheduler extends AbstractScheduler {
|
||||
private static final ImmediateScheduler INSTANCE = new ImmediateScheduler();
|
||||
|
||||
public static ImmediateScheduler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private ImmediateScheduler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action) {
|
||||
return action.call();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
|
||||
return schedule(new SleepingAction(action, this, dueTime, unit));
|
||||
}
|
||||
|
||||
}
|
||||
49
HMCLAPI/src/main/java/rx/concurrency/NewThreadScheduler.java
Normal file
49
HMCLAPI/src/main/java/rx/concurrency/NewThreadScheduler.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.concurrency;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/**
|
||||
* Schedules work on a new thread.
|
||||
*/
|
||||
public class NewThreadScheduler extends AbstractScheduler {
|
||||
private static final NewThreadScheduler INSTANCE = new NewThreadScheduler();
|
||||
|
||||
public static NewThreadScheduler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action) {
|
||||
final DiscardableAction discardableAction = new DiscardableAction(action);
|
||||
|
||||
Thread t = new Thread(discardableAction::call, "RxNewThreadScheduler");
|
||||
|
||||
t.start();
|
||||
|
||||
return discardableAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
|
||||
return schedule(new SleepingAction(action, this, dueTime, unit));
|
||||
}
|
||||
|
||||
}
|
||||
143
HMCLAPI/src/main/java/rx/concurrency/Schedulers.java
Normal file
143
HMCLAPI/src/main/java/rx/concurrency/Schedulers.java
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* 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.concurrency;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import rx.Scheduler;
|
||||
|
||||
/**
|
||||
* Static factory methods for creating Schedulers.
|
||||
*/
|
||||
public class Schedulers {
|
||||
private static final ScheduledExecutorService COMPUTATION_EXECUTOR = createComputationExecutor();
|
||||
private static final Executor IO_EXECUTOR = createIOExecutor();
|
||||
|
||||
private Schedulers() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that executes work immediately on the current thread.
|
||||
*
|
||||
* @return {@link ImmediateScheduler} instance
|
||||
*/
|
||||
public static Scheduler immediate() {
|
||||
return ImmediateScheduler.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that queues work on the current thread to be executed after the current work completes.
|
||||
*
|
||||
* @return {@link CurrentThreadScheduler} instance
|
||||
*/
|
||||
public static Scheduler currentThread() {
|
||||
return CurrentThreadScheduler.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that creates a new {@link Thread} for each unit of work.
|
||||
*
|
||||
* @return {@link NewThreadScheduler} instance
|
||||
*/
|
||||
public static Scheduler newThread() {
|
||||
return NewThreadScheduler.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that queues work on an {@link Executor}.
|
||||
* <p>
|
||||
* Note that this does not support scheduled actions with a delay.
|
||||
*
|
||||
* @return {@link ExecutorScheduler} instance
|
||||
*/
|
||||
public static Scheduler executor(Executor executor) {
|
||||
return new ExecutorScheduler(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} that queues work on an {@link ScheduledExecutorService}.
|
||||
*
|
||||
* @return {@link ExecutorScheduler} instance
|
||||
*/
|
||||
public static Scheduler executor(ScheduledExecutorService executor) {
|
||||
return new ExecutorScheduler(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} intended for computational work.
|
||||
* <p>
|
||||
* The implementation is backed by a {@link ScheduledExecutorService} thread-pool sized to the number of CPU cores.
|
||||
* <p>
|
||||
* This can be used for event-loops, processing callbacks and other computational work.
|
||||
* <p>
|
||||
* Do not perform IO-bound work on this scheduler. Use {@link #threadPoolForComputation()} instead.
|
||||
*
|
||||
* @return {@link ExecutorScheduler} for computation-bound work.
|
||||
*/
|
||||
public static Scheduler threadPoolForComputation() {
|
||||
return executor(COMPUTATION_EXECUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Scheduler} intended for IO-bound work.
|
||||
* <p>
|
||||
* The implementation is backed by an {@link Executor} thread-pool that will grow as needed.
|
||||
* <p>
|
||||
* This can be used for asynchronously performing blocking IO.
|
||||
* <p>
|
||||
* Do not perform computational work on this scheduler. Use {@link #threadPoolForComputation()} instead.
|
||||
*
|
||||
* @return {@link ExecutorScheduler} for IO-bound work.
|
||||
*/
|
||||
public static Scheduler threadPoolForIO() {
|
||||
return executor(IO_EXECUTOR);
|
||||
}
|
||||
|
||||
private static ScheduledExecutorService createComputationExecutor() {
|
||||
int cores = Runtime.getRuntime().availableProcessors();
|
||||
return Executors.newScheduledThreadPool(cores, new ThreadFactory() {
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r, "RxComputationThreadPool-" + counter.incrementAndGet());
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static Executor createIOExecutor() {
|
||||
Executor result = Executors.newCachedThreadPool(new ThreadFactory() {
|
||||
final AtomicLong counter = new AtomicLong();
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r, "RxIOThreadPool-" + counter.incrementAndGet());
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
49
HMCLAPI/src/main/java/rx/concurrency/SleepingAction.java
Normal file
49
HMCLAPI/src/main/java/rx/concurrency/SleepingAction.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.concurrency;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Scheduler;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
|
||||
/* package */class SleepingAction implements Func0<Subscription> {
|
||||
private final Func0<Subscription> underlying;
|
||||
private final Scheduler scheduler;
|
||||
private final long execTime;
|
||||
|
||||
public SleepingAction(Func0<Subscription> underlying, Scheduler scheduler, long timespan, TimeUnit timeUnit) {
|
||||
this.underlying = underlying;
|
||||
this.scheduler = scheduler;
|
||||
this.execTime = scheduler.now() + timeUnit.toMillis(timespan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call() {
|
||||
if (execTime < scheduler.now()) {
|
||||
try {
|
||||
Thread.sleep(scheduler.now() - execTime);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return underlying.call();
|
||||
|
||||
}
|
||||
}
|
||||
19
HMCLAPI/src/main/java/rx/concurrency/package-info.java
Normal file
19
HMCLAPI/src/main/java/rx/concurrency/package-info.java
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* Rx Schedulers
|
||||
*/
|
||||
package rx.concurrency;
|
||||
43
HMCLAPI/src/main/java/rx/observables/GroupedObservable.java
Normal file
43
HMCLAPI/src/main/java/rx/observables/GroupedObservable.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.observables;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* An {@link Observable} that has been grouped by a key whose value can be obtained using {@link #getKey()} <p>
|
||||
*
|
||||
* @see {@link Observable#groupBy(Observable, Func1)}
|
||||
*
|
||||
* @param <K>
|
||||
* @param <T>
|
||||
*/
|
||||
public class GroupedObservable<K, T> extends Observable<T> {
|
||||
private final K key;
|
||||
|
||||
public GroupedObservable(K key, Func1<Observer<T>, Subscription> onSubscribe) {
|
||||
super(onSubscribe);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
}
|
||||
61
HMCLAPI/src/main/java/rx/operators/OperationAll.java
Normal file
61
HMCLAPI/src/main/java/rx/operators/OperationAll.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package rx.operators;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class OperationAll {
|
||||
|
||||
public static <T> Func1<Observer<Boolean>, Subscription> all(Observable<T> sequence, Func1<T, Boolean> predicate) {
|
||||
return new AllObservable<>(sequence, predicate);
|
||||
}
|
||||
|
||||
private static class AllObservable<T> implements Func1<Observer<Boolean>, Subscription> {
|
||||
private final Observable<T> sequence;
|
||||
private final Func1<T, Boolean> predicate;
|
||||
|
||||
private final AtomicBoolean status = new AtomicBoolean(true);
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
|
||||
private AllObservable(Observable<T> sequence, Func1<T, Boolean> predicate) {
|
||||
this.sequence = sequence;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<Boolean> observer) {
|
||||
return subscription.wrap(sequence.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (status.get()) {
|
||||
observer.onNext(true);
|
||||
observer.onCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
boolean result = predicate.call(args);
|
||||
boolean changed = status.compareAndSet(true, result);
|
||||
|
||||
if (changed && !result) {
|
||||
observer.onNext(false);
|
||||
observer.onCompleted();
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
273
HMCLAPI/src/main/java/rx/operators/OperationCombineLatest.java
Normal file
273
HMCLAPI/src/main/java/rx/operators/OperationCombineLatest.java
Normal file
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* 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.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
import rx.util.functions.Func2;
|
||||
import rx.util.functions.Func3;
|
||||
import rx.util.functions.Func4;
|
||||
import rx.util.functions.FuncN;
|
||||
import rx.util.functions.Functions;
|
||||
|
||||
public class OperationCombineLatest {
|
||||
|
||||
/**
|
||||
* Combines the two given observables, emitting an event containing an aggregation of the latest values of each of the source observables
|
||||
* each time an event is received from one of the source observables, where the aggregation is defined by the given function.
|
||||
* @param w0 The first source observable.
|
||||
* @param w1 The second source observable.
|
||||
* @param combineLatestFunction The aggregation function used to combine the source observable values.
|
||||
* @return A function from an observer to a subscription. This can be used to create an observable from.
|
||||
*/
|
||||
public static <T0, T1, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Func2<T0, T1, R> combineLatestFunction) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
|
||||
a.addObserver(new CombineObserver<R, T0>(a, w0));
|
||||
a.addObserver(new CombineObserver<R, T1>(a, w1));
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction)
|
||||
*/
|
||||
public static <T0, T1, T2, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Func3<T0, T1, T2, R> combineLatestFunction) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
|
||||
a.addObserver(new CombineObserver<R, T0>(a, w0));
|
||||
a.addObserver(new CombineObserver<R, T1>(a, w1));
|
||||
a.addObserver(new CombineObserver<R, T2>(a, w2));
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction)
|
||||
*/
|
||||
public static <T0, T1, T2, T3, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Observable<T3> w3, Func4<T0, T1, T2, T3, R> combineLatestFunction) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
|
||||
a.addObserver(new CombineObserver<R, T0>(a, w0));
|
||||
a.addObserver(new CombineObserver<R, T1>(a, w1));
|
||||
a.addObserver(new CombineObserver<R, T2>(a, w2));
|
||||
a.addObserver(new CombineObserver<R, T3>(a, w3));
|
||||
return a;
|
||||
}
|
||||
|
||||
private static class CombineObserver<R, T> implements Observer<T> {
|
||||
final Observable<T> w;
|
||||
final Aggregator<R> a;
|
||||
private Subscription subscription;
|
||||
|
||||
public CombineObserver(Aggregator<R> a, Observable<T> w) {
|
||||
this.a = a;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
public synchronized void startWatching() {
|
||||
if (subscription != null) {
|
||||
throw new RuntimeException("This should only be called once.");
|
||||
}
|
||||
subscription = w.subscribe(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
a.complete(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
a.error(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
a.next(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive notifications from each of the observables we are reducing and execute the combineLatestFunction
|
||||
* whenever we have received an event from one of the observables, as soon as each Observable has received
|
||||
* at least one event.
|
||||
*/
|
||||
private static class Aggregator<R> implements Func1<Observer<R>, Subscription> {
|
||||
|
||||
private Observer<R> observer;
|
||||
|
||||
private final FuncN<R> combineLatestFunction;
|
||||
private final AtomicBoolean running = new AtomicBoolean(true);
|
||||
|
||||
// used as an internal lock for handling the latest values and the completed state of each observer
|
||||
private final Object lockObject = new Object();
|
||||
|
||||
/**
|
||||
* Store when an observer completes.
|
||||
* <p>
|
||||
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject' above.
|
||||
* */
|
||||
private final Set<CombineObserver<R, ?>> completed = new HashSet<>();
|
||||
|
||||
/**
|
||||
* The latest value from each observer
|
||||
* <p>
|
||||
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject' above.
|
||||
* */
|
||||
private final Map<CombineObserver<R, ?>, Object> latestValue = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Whether each observer has a latest value at all.
|
||||
* <p>
|
||||
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject' above.
|
||||
* */
|
||||
private final Set<CombineObserver<R, ?>> hasLatestValue = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Ordered list of observers to combine.
|
||||
* No synchronization is necessary as these can not be added or changed asynchronously.
|
||||
*/
|
||||
private final List<CombineObserver<R, ?>> observers = new LinkedList<>();
|
||||
|
||||
public Aggregator(FuncN<R> combineLatestFunction) {
|
||||
this.combineLatestFunction = combineLatestFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive notification of a Observer starting (meaning we should require it for aggregation)
|
||||
*
|
||||
* @param w The observer to add.
|
||||
*/
|
||||
<T> void addObserver(CombineObserver<R, T> w) {
|
||||
observers.add(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive notification of a Observer completing its iterations.
|
||||
*
|
||||
* @param w The observer that has completed.
|
||||
*/
|
||||
<T> void complete(CombineObserver<R, T> w) {
|
||||
synchronized(lockObject) {
|
||||
// store that this CombineLatestObserver is completed
|
||||
completed.add(w);
|
||||
// if all CombineObservers are completed, we mark the whole thing as completed
|
||||
if (completed.size() == observers.size()) {
|
||||
if (running.get()) {
|
||||
// mark ourselves as done
|
||||
observer.onCompleted();
|
||||
// just to ensure we stop processing in case we receive more onNext/complete/error calls after this
|
||||
running.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive error for a Observer. Throw the error up the chain and stop processing.
|
||||
*/
|
||||
void error(Exception e) {
|
||||
observer.onError(e);
|
||||
/* tell all observers to unsubscribe since we had an error */
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the next value from an observer.
|
||||
* <p>
|
||||
* If we have received values from all observers, trigger the combineLatest function, otherwise store the value and keep waiting.
|
||||
*
|
||||
* @param w
|
||||
* @param arg
|
||||
*/
|
||||
<T> void next(CombineObserver<R, T> w, T arg) {
|
||||
if (observer == null) {
|
||||
throw new RuntimeException("This shouldn't be running if an Observer isn't registered");
|
||||
}
|
||||
|
||||
/* if we've been 'unsubscribed' don't process anything further even if the things we're watching keep sending (likely because they are not responding to the unsubscribe call) */
|
||||
if (!running.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// define here so the variable is out of the synchronized scope
|
||||
Object[] argsToCombineLatest = new Object[observers.size()];
|
||||
|
||||
// we synchronize everything that touches latest values
|
||||
synchronized (lockObject) {
|
||||
// remember this as the latest value for this observer
|
||||
latestValue.put(w, arg);
|
||||
|
||||
// remember that this observer now has a latest value set
|
||||
hasLatestValue.add(w);
|
||||
|
||||
// if all observers in the 'observers' list have a value, invoke the combineLatestFunction
|
||||
for (CombineObserver<R, ?> rw : observers) {
|
||||
if (!hasLatestValue.contains(rw)) {
|
||||
// we don't have a value yet for each observer to combine, so we don't have a combined value yet either
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if we get to here this means all the queues have data
|
||||
int i = 0;
|
||||
for (CombineObserver<R, ?> _w : observers) {
|
||||
argsToCombineLatest[i++] = latestValue.get(_w);
|
||||
}
|
||||
}
|
||||
// if we did not return above from the synchronized block we can now invoke the combineLatestFunction with all of the args
|
||||
// we do this outside the synchronized block as it is now safe to call this concurrently and don't need to block other threads from calling
|
||||
// this 'next' method while another thread finishes calling this combineLatestFunction
|
||||
observer.onNext(combineLatestFunction.call(argsToCombineLatest));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<R> observer) {
|
||||
if (this.observer != null) {
|
||||
throw new IllegalStateException("Only one Observer can subscribe to this Observable.");
|
||||
}
|
||||
this.observer = observer;
|
||||
|
||||
/* start the observers */
|
||||
for (CombineObserver<R, ?> rw : observers) {
|
||||
rw.startWatching();
|
||||
}
|
||||
|
||||
return new Subscription() {
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
stop();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
/* tell ourselves to stop processing onNext events */
|
||||
running.set(false);
|
||||
/* propogate to all observers to unsubscribe */
|
||||
for (CombineObserver<R, ?> rw : observers) {
|
||||
if (rw.subscription != null) {
|
||||
rw.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
126
HMCLAPI/src/main/java/rx/operators/OperationConcat.java
Normal file
126
HMCLAPI/src/main/java/rx/operators/OperationConcat.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Action1;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationConcat {
|
||||
|
||||
/**
|
||||
* Combine the observable sequences from the list of Observables into one observable sequence without any transformation.
|
||||
*
|
||||
* @param sequences
|
||||
* An observable sequence of elements to project.
|
||||
* @return An observable sequence whose elements are the result of combining the output from the list of Observables.
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> concat(final Observable<T>... sequences) {
|
||||
return new Func1<Observer<T>, Subscription>() {
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return new Concat<T>(sequences).call(observer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> concat(final List<Observable<T>> sequences) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Observable<T>[] o = sequences.toArray((Observable<T>[]) Array.newInstance(Observable.class, sequences.size()));
|
||||
return concat(o);
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> concat(final Observable<Observable<T>> sequences) {
|
||||
final List<Observable<T>> list = new ArrayList<Observable<T>>();
|
||||
sequences.toList().subscribe(new Action1<List<Observable<T>>>() {
|
||||
@Override
|
||||
public void call(List<Observable<T>> t1) {
|
||||
list.addAll(t1);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return concat(list);
|
||||
}
|
||||
|
||||
private static class Concat<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final Observable<T>[] sequences;
|
||||
private int num = 0;
|
||||
private int count = 0;
|
||||
private Subscription s;
|
||||
|
||||
Concat(final Observable<T>... sequences) {
|
||||
this.sequences = sequences;
|
||||
this.num = sequences.length - 1;
|
||||
}
|
||||
|
||||
private final AtomicObservableSubscription Subscription = new AtomicObservableSubscription();
|
||||
|
||||
private final Subscription actualSubscription = new Subscription() {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
if (null != s)
|
||||
s.unsubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
public Subscription call(Observer<T> observer) {
|
||||
s = sequences[count].subscribe(new ConcatObserver(observer));
|
||||
|
||||
return Subscription.wrap(actualSubscription);
|
||||
}
|
||||
|
||||
private class ConcatObserver implements Observer<T> {
|
||||
private final Observer<T> observer;
|
||||
|
||||
ConcatObserver(Observer<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (num == count)
|
||||
observer.onCompleted();
|
||||
else {
|
||||
count++;
|
||||
s = sequences[count].subscribe(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
observer.onNext(args);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
HMCLAPI/src/main/java/rx/operators/OperationDefer.java
Normal file
30
HMCLAPI/src/main/java/rx/operators/OperationDefer.java
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func0;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationDefer {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> defer(final Func0<Observable<T>> observableFactory) {
|
||||
return observableFactory.call()::subscribe;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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 rx.Notification;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Dematerializes the explicit notification values of an observable sequence as implicit notifications.
|
||||
* See http://msdn.microsoft.com/en-us/library/hh229047(v=vs.103).aspx for the Microsoft Rx equivalent.
|
||||
*/
|
||||
public final class OperationDematerialize {
|
||||
|
||||
/**
|
||||
* Dematerializes the explicit notification values of an observable sequence as implicit notifications.
|
||||
*
|
||||
* @param sequence
|
||||
* An observable sequence containing explicit notification values which have to be turned into implicit notifications.
|
||||
* @return An observable sequence exhibiting the behavior corresponding to the source sequence's notification values.
|
||||
* @see http://msdn.microsoft.com/en-us/library/hh229047(v=vs.103).aspx
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> dematerialize(final Observable<Notification<T>> sequence) {
|
||||
return new DematerializeObservable<>(sequence);
|
||||
}
|
||||
|
||||
private static class DematerializeObservable<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<Notification<T>> sequence;
|
||||
|
||||
public DematerializeObservable(Observable<Notification<T>> sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
return sequence.subscribe(new Observer<Notification<T>>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Notification<T> value) {
|
||||
switch (value.getKind()) {
|
||||
case OnNext:
|
||||
observer.onNext(value.getValue());
|
||||
break;
|
||||
case OnError:
|
||||
observer.onError(value.getException());
|
||||
break;
|
||||
case OnCompleted:
|
||||
observer.onCompleted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
70
HMCLAPI/src/main/java/rx/operators/OperationFilter.java
Normal file
70
HMCLAPI/src/main/java/rx/operators/OperationFilter.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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 rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationFilter<T> {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> filter(Observable<T> that, Func1<T, Boolean> predicate) {
|
||||
return new Filter<>(that, predicate);
|
||||
}
|
||||
|
||||
private static class Filter<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<T> that;
|
||||
private final Func1<T, Boolean> predicate;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
public Filter(Observable<T> that, Func1<T, Boolean> predicate) {
|
||||
this.that = that;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
return subscription.wrap(that.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
try {
|
||||
if (predicate.call(value)) {
|
||||
observer.onNext(value);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
observer.onError(ex);
|
||||
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
91
HMCLAPI/src/main/java/rx/operators/OperationFinally.java
Normal file
91
HMCLAPI/src/main/java/rx/operators/OperationFinally.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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 rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Action0;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationFinally {
|
||||
|
||||
/**
|
||||
* Call a given action when a sequence completes (with or without an
|
||||
* exception). The returned observable is exactly as threadsafe as the
|
||||
* source observable.
|
||||
* <p/>
|
||||
* Note that "finally" is a Java reserved word and cannot be an identifier,
|
||||
* so we use "finallyDo".
|
||||
*
|
||||
* @param sequence An observable sequence of elements
|
||||
* @param action An action to be taken when the sequence is complete or throws an exception
|
||||
* @return An observable sequence with the same elements as the input.
|
||||
* After the last element is consumed (and {@link Observer#onCompleted} has been called),
|
||||
* or after an exception is thrown (and {@link Observer#onError} has been called),
|
||||
* the given action will be called.
|
||||
* @see <a href="http://msdn.microsoft.com/en-us/library/hh212133(v=vs.103).aspx">MSDN Observable.Finally method</a>
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> finallyDo(final Observable<T> sequence, final Action0 action) {
|
||||
return new Finally<>(sequence, action)::call;
|
||||
}
|
||||
|
||||
private static class Finally<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final Observable<T> sequence;
|
||||
private final Action0 finalAction;
|
||||
|
||||
Finally(final Observable<T> sequence, Action0 finalAction) {
|
||||
this.sequence = sequence;
|
||||
this.finalAction = finalAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return sequence.subscribe(new FinallyObserver(observer));
|
||||
}
|
||||
|
||||
private class FinallyObserver implements Observer<T> {
|
||||
private final Observer<T> observer;
|
||||
|
||||
FinallyObserver(Observer<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
try {
|
||||
observer.onCompleted();
|
||||
} finally {
|
||||
finalAction.call();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
try {
|
||||
observer.onError(e);
|
||||
} finally {
|
||||
finalAction.call();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
observer.onNext(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
HMCLAPI/src/main/java/rx/operators/OperationMap.java
Normal file
124
HMCLAPI/src/main/java/rx/operators/OperationMap.java
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* 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 rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationMap {
|
||||
|
||||
/**
|
||||
* Accepts a sequence and a transformation function. Returns a sequence that is the result of
|
||||
* applying the transformation function to each item in the sequence.
|
||||
*
|
||||
* @param sequence
|
||||
* the input sequence.
|
||||
* @param func
|
||||
* a function to apply to each item in the sequence.
|
||||
* @param <T>
|
||||
* the type of the input sequence.
|
||||
* @param <R>
|
||||
* the type of the output sequence.
|
||||
* @return a sequence that is the result of applying the transformation function to each item in the input sequence.
|
||||
*/
|
||||
public static <T, R> Func1<Observer<R>, Subscription> map(Observable<T> sequence, Func1<T, R> func) {
|
||||
return new MapObservable<>(sequence, func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a sequence of observable sequences and a transformation function. Returns a flattened sequence that is the result of
|
||||
* applying the transformation function to each item in the sequence of each observable sequence.
|
||||
* <p>
|
||||
* The closure should return an Observable which will then be merged.
|
||||
*
|
||||
* @param sequence
|
||||
* the input sequence.
|
||||
* @param func
|
||||
* a function to apply to each item in the sequence.
|
||||
* @param <T>
|
||||
* the type of the input sequence.
|
||||
* @param <R>
|
||||
* the type of the output sequence.
|
||||
* @return a sequence that is the result of applying the transformation function to each item in the input sequence.
|
||||
*/
|
||||
public static <T, R> Func1<Observer<R>, Subscription> mapMany(Observable<T> sequence, Func1<T, Observable<R>> func) {
|
||||
return OperationMerge.merge(Observable.create(map(sequence, func)));
|
||||
}
|
||||
|
||||
/**
|
||||
* An observable sequence that is the result of applying a transformation to each item in an input sequence.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the input sequence.
|
||||
* @param <R>
|
||||
* the type of the output sequence.
|
||||
*/
|
||||
private static class MapObservable<T, R> implements Func1<Observer<R>, Subscription> {
|
||||
public MapObservable(Observable<T> sequence, Func1<T, R> func) {
|
||||
this.sequence = sequence;
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
private final Observable<T> sequence;
|
||||
|
||||
private final Func1<T, R> func;
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<R> observer) {
|
||||
return sequence.subscribe(new MapObserver<>(observer, func));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An observer that applies a transformation function to each item and forwards the result to an inner observer.
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the observer items.
|
||||
* @param <R>
|
||||
* the type of the inner observer items.
|
||||
*/
|
||||
private static class MapObserver<T, R> implements Observer<T> {
|
||||
public MapObserver(Observer<R> observer, Func1<T, R> func) {
|
||||
this.observer = observer;
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
Observer<R> observer;
|
||||
|
||||
Func1<T, R> func;
|
||||
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
try {
|
||||
observer.onNext(func.call(value));
|
||||
} catch (Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
78
HMCLAPI/src/main/java/rx/operators/OperationMaterialize.java
Normal file
78
HMCLAPI/src/main/java/rx/operators/OperationMaterialize.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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 rx.Notification;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Materializes the implicit notifications of an observable sequence as explicit notification values.
|
||||
* <p>
|
||||
* In other words, converts a sequence of OnNext, OnError and OnCompleted events into a sequence of ObservableNotifications containing the OnNext, OnError and OnCompleted values.
|
||||
* <p>
|
||||
* See http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx for the Microsoft Rx equivalent.
|
||||
*/
|
||||
public final class OperationMaterialize {
|
||||
|
||||
/**
|
||||
* Materializes the implicit notifications of an observable sequence as explicit notification values.
|
||||
*
|
||||
* @param sequence
|
||||
* An observable sequence of elements to project.
|
||||
* @return An observable sequence whose elements are the result of materializing the notifications of the given sequence.
|
||||
* @see http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx
|
||||
*/
|
||||
public static <T> Func1<Observer<Notification<T>>, Subscription> materialize(final Observable<T> sequence) {
|
||||
return new MaterializeObservable<>(sequence);
|
||||
}
|
||||
|
||||
private static class MaterializeObservable<T> implements Func1<Observer<Notification<T>>, Subscription> {
|
||||
|
||||
private final Observable<T> sequence;
|
||||
|
||||
public MaterializeObservable(Observable<T> sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<Notification<T>> observer) {
|
||||
return sequence.subscribe(new Observer<T>() {
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onNext(new Notification<>());
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onNext(new Notification<>(e));
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
observer.onNext(new Notification<>(value));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
255
HMCLAPI/src/main/java/rx/operators/OperationMerge.java
Normal file
255
HMCLAPI/src/main/java/rx/operators/OperationMerge.java
Normal file
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* 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.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.SynchronizedObserver;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationMerge {
|
||||
|
||||
/**
|
||||
* Flattens the observable sequences from the list of Observables into one observable sequence without any transformation.
|
||||
*
|
||||
* @param source
|
||||
* An observable sequence of elements to project.
|
||||
* @return An observable sequence whose elements are the result of flattening the output from the list of Observables.
|
||||
* @see http://msdn.microsoft.com/en-us/library/hh229099(v=vs.103).aspx
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> merge(final Observable<Observable<T>> source) {
|
||||
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
|
||||
return new MergeObservable<T>(source)::call;
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> merge(final Observable<T>... sequences) {
|
||||
return merge(Arrays.asList(sequences));
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> merge(final List<Observable<T>> sequences) {
|
||||
return merge(Observable.create(new Func1<Observer<Observable<T>>, Subscription>() {
|
||||
|
||||
private volatile boolean unsubscribed = false;
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<Observable<T>> observer) {
|
||||
for (Observable<T> o : sequences) {
|
||||
if (!unsubscribed) {
|
||||
observer.onNext(o);
|
||||
} else {
|
||||
// break out of the loop if we are unsubscribed
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!unsubscribed) {
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
return () -> {
|
||||
unsubscribed = true;
|
||||
};
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
|
||||
* <p>
|
||||
* It IS thread-safe from within it while receiving onNext events from multiple threads.
|
||||
* <p>
|
||||
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above.
|
||||
* <p>
|
||||
* Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private static final class MergeObservable<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final Observable<Observable<T>> sequences;
|
||||
private final MergeSubscription ourSubscription = new MergeSubscription();
|
||||
private final AtomicBoolean stopped = new AtomicBoolean(false);
|
||||
private volatile boolean parentCompleted = false;
|
||||
private final ConcurrentHashMap<ChildObserver, ChildObserver> childObservers = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<ChildObserver, Subscription> childSubscriptions = new ConcurrentHashMap<>();
|
||||
|
||||
private MergeObservable(Observable<Observable<T>> sequences) {
|
||||
this.sequences = sequences;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> actualObserver) {
|
||||
|
||||
/**
|
||||
* We must synchronize a merge because we subscribe to multiple sequences in parallel that will each be emitting.
|
||||
* <p>
|
||||
* The calls from each sequence must be serialized.
|
||||
* <p>
|
||||
* Bug report: https://github.com/Netflix/RxJava/issues/200
|
||||
*/
|
||||
AtomicObservableSubscription subscription = new AtomicObservableSubscription(ourSubscription);
|
||||
SynchronizedObserver<T> synchronizedObserver = new SynchronizedObserver<>(actualObserver, subscription);
|
||||
|
||||
/**
|
||||
* Subscribe to the parent Observable to get to the children Observables
|
||||
*/
|
||||
sequences.subscribe(new ParentObserver(synchronizedObserver));
|
||||
|
||||
/* return our subscription to allow unsubscribing */
|
||||
return subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage the internal subscription with a thread-safe means of stopping/unsubscribing so we don't unsubscribe twice.
|
||||
* <p>
|
||||
* Also has the stop() method returning a boolean so callers know if their thread "won" and should perform further actions.
|
||||
*/
|
||||
private class MergeSubscription implements Subscription {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
stop();
|
||||
}
|
||||
|
||||
public boolean stop() {
|
||||
// try setting to false unless another thread beat us
|
||||
boolean didSet = stopped.compareAndSet(false, true);
|
||||
if (didSet) {
|
||||
// this thread won the race to stop, so unsubscribe from the actualSubscription
|
||||
for (Subscription _s : childSubscriptions.values()) {
|
||||
_s.unsubscribe();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// another thread beat us
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to the top level Observable to receive the sequence of Observable<T> children.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private class ParentObserver implements Observer<Observable<T>> {
|
||||
private final Observer<T> actualObserver;
|
||||
|
||||
public ParentObserver(Observer<T> actualObserver) {
|
||||
this.actualObserver = actualObserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
parentCompleted = true;
|
||||
// this *can* occur before the children are done, so if it does we won't send onCompleted
|
||||
// but will let the child worry about it
|
||||
// if however this completes and there are no children processing, then we will send onCompleted
|
||||
|
||||
if (childObservers.isEmpty()) {
|
||||
if (!stopped.get()) {
|
||||
if (ourSubscription.stop()) {
|
||||
actualObserver.onCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
actualObserver.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Observable<T> childObservable) {
|
||||
if (stopped.get()) {
|
||||
// we won't act on any further items
|
||||
return;
|
||||
}
|
||||
|
||||
if (childObservable == null) {
|
||||
throw new IllegalArgumentException("Observable<T> can not be null.");
|
||||
}
|
||||
|
||||
/**
|
||||
* For each child Observable we receive we'll subscribe with a separate Observer
|
||||
* that will each then forward their sequences to the actualObserver.
|
||||
* <p>
|
||||
* We use separate child Observers for each sequence to simplify the onComplete/onError handling so each sequence has its own lifecycle.
|
||||
*/
|
||||
ChildObserver _w = new ChildObserver(actualObserver);
|
||||
childObservers.put(_w, _w);
|
||||
Subscription _subscription = childObservable.subscribe(_w);
|
||||
// remember this Observer and the subscription from it
|
||||
childSubscriptions.put(_w, _subscription);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to each child Observable<T> and forward their sequence of data to the actualObserver
|
||||
*
|
||||
*/
|
||||
private class ChildObserver implements Observer<T> {
|
||||
|
||||
private final Observer<T> actualObserver;
|
||||
|
||||
public ChildObserver(Observer<T> actualObserver) {
|
||||
this.actualObserver = actualObserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// remove self from map of Observers
|
||||
childObservers.remove(this);
|
||||
// if there are now 0 Observers left, so if the parent is also completed we send the onComplete to the actualObserver
|
||||
// if the parent is not complete that means there is another sequence (and child Observer) to come
|
||||
if (!stopped.get()) {
|
||||
if (childObservers.isEmpty() && parentCompleted) {
|
||||
if (ourSubscription.stop()) {
|
||||
// this thread 'won' the race to unsubscribe/stop so let's send onCompleted
|
||||
actualObserver.onCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
if (!stopped.get()) {
|
||||
if (ourSubscription.stop()) {
|
||||
// this thread 'won' the race to unsubscribe/stop so let's send the error
|
||||
actualObserver.onError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
// in case the Observable is poorly behaved and doesn't listen to the unsubscribe request
|
||||
// we'll ignore anything that comes in after we've unsubscribed
|
||||
if (!stopped.get()) {
|
||||
actualObserver.onNext(args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
327
HMCLAPI/src/main/java/rx/operators/OperationMergeDelayError.java
Normal file
327
HMCLAPI/src/main/java/rx/operators/OperationMergeDelayError.java
Normal file
@@ -0,0 +1,327 @@
|
||||
/**
|
||||
* 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.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.CompositeException;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Same functionality as OperationMerge except that onError events will be skipped so that all onNext calls are passed on until all sequences finish with onComplete or onError, and then the first
|
||||
* onError received (if any) will be passed on.
|
||||
* <p>
|
||||
* This allows retrieving all successful onNext calls without being blocked by an onError early in a sequence.
|
||||
* <p>
|
||||
* NOTE: If this is used on an infinite stream it will never call onError and effectively will swallow errors.
|
||||
*/
|
||||
public final class OperationMergeDelayError {
|
||||
|
||||
/**
|
||||
* Flattens the observable sequences from the list of Observables into one observable sequence without any transformation and delays any onError calls until after all sequences have called
|
||||
* onError or onComplete so as to allow all successful
|
||||
* onNext calls to be received.
|
||||
*
|
||||
* @param source
|
||||
* An observable sequence of elements to project.
|
||||
* @return An observable sequence whose elements are the result of flattening the output from the list of Observables.
|
||||
* @see http://msdn.microsoft.com/en-us/library/hh229099(v=vs.103).aspx
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> mergeDelayError(final Observable<Observable<T>> sequences) {
|
||||
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
|
||||
return new Func1<Observer<T>, Subscription>() {
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return new MergeDelayErrorObservable<T>(sequences).call(observer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> mergeDelayError(final Observable<T>... sequences) {
|
||||
return mergeDelayError(Observable.create(new Func1<Observer<Observable<T>>, Subscription>() {
|
||||
private volatile boolean unsubscribed = false;
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<Observable<T>> observer) {
|
||||
for (Observable<T> o : sequences) {
|
||||
if (!unsubscribed) {
|
||||
observer.onNext(o);
|
||||
} else {
|
||||
// break out of the loop if we are unsubscribed
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!unsubscribed) {
|
||||
observer.onCompleted();
|
||||
}
|
||||
return new Subscription() {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
unsubscribed = true;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> mergeDelayError(final List<Observable<T>> sequences) {
|
||||
return mergeDelayError(Observable.create(new Func1<Observer<Observable<T>>, Subscription>() {
|
||||
|
||||
private volatile boolean unsubscribed = false;
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<Observable<T>> observer) {
|
||||
for (Observable<T> o : sequences) {
|
||||
if (!unsubscribed) {
|
||||
observer.onNext(o);
|
||||
} else {
|
||||
// break out of the loop if we are unsubscribed
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!unsubscribed) {
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
return new Subscription() {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
unsubscribed = true;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
|
||||
* <p>
|
||||
* It IS thread-safe from within it while receiving onNext events from multiple threads.
|
||||
* <p>
|
||||
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above.
|
||||
* <p>
|
||||
* Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private static final class MergeDelayErrorObservable<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final Observable<Observable<T>> sequences;
|
||||
private final MergeSubscription ourSubscription = new MergeSubscription();
|
||||
private AtomicBoolean stopped = new AtomicBoolean(false);
|
||||
private volatile boolean parentCompleted = false;
|
||||
private final ConcurrentHashMap<ChildObserver, ChildObserver> childObservers = new ConcurrentHashMap<ChildObserver, ChildObserver>();
|
||||
private final ConcurrentHashMap<ChildObserver, Subscription> childSubscriptions = new ConcurrentHashMap<ChildObserver, Subscription>();
|
||||
// onErrors we received that will be delayed until everything is completed and then sent
|
||||
private ConcurrentLinkedQueue<Exception> onErrorReceived = new ConcurrentLinkedQueue<Exception>();
|
||||
|
||||
private MergeDelayErrorObservable(Observable<Observable<T>> sequences) {
|
||||
this.sequences = sequences;
|
||||
}
|
||||
|
||||
public Subscription call(Observer<T> actualObserver) {
|
||||
/**
|
||||
* Subscribe to the parent Observable to get to the children Observables
|
||||
*/
|
||||
sequences.subscribe(new ParentObserver(actualObserver));
|
||||
|
||||
/* return our subscription to allow unsubscribing */
|
||||
return ourSubscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage the internal subscription with a thread-safe means of stopping/unsubscribing so we don't unsubscribe twice.
|
||||
* <p>
|
||||
* Also has the stop() method returning a boolean so callers know if their thread "won" and should perform further actions.
|
||||
*/
|
||||
private class MergeSubscription implements Subscription {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
stop();
|
||||
}
|
||||
|
||||
public boolean stop() {
|
||||
// try setting to false unless another thread beat us
|
||||
boolean didSet = stopped.compareAndSet(false, true);
|
||||
if (didSet) {
|
||||
// this thread won the race to stop, so unsubscribe from the actualSubscription
|
||||
for (Subscription _s : childSubscriptions.values()) {
|
||||
_s.unsubscribe();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// another thread beat us
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to the top level Observable to receive the sequence of Observable<T> children.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private class ParentObserver implements Observer<Observable<T>> {
|
||||
private final Observer<T> actualObserver;
|
||||
|
||||
public ParentObserver(Observer<T> actualObserver) {
|
||||
this.actualObserver = actualObserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
parentCompleted = true;
|
||||
// this *can* occur before the children are done, so if it does we won't send onCompleted
|
||||
// but will let the child worry about it
|
||||
// if however this completes and there are no children processing, then we will send onCompleted
|
||||
|
||||
if (childObservers.size() == 0) {
|
||||
if (!stopped.get()) {
|
||||
if (ourSubscription.stop()) {
|
||||
if (onErrorReceived.size() == 1) {
|
||||
// an onError was received from 1 ChildObserver so we now send it as a delayed error
|
||||
actualObserver.onError(onErrorReceived.peek());
|
||||
} else if (onErrorReceived.size() > 1) {
|
||||
// an onError was received from more than 1 ChildObserver so we now send it as a delayed error
|
||||
actualObserver.onError(new CompositeException(onErrorReceived));
|
||||
} else {
|
||||
// no delayed error so send onCompleted
|
||||
actualObserver.onCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
actualObserver.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Observable<T> childObservable) {
|
||||
if (stopped.get()) {
|
||||
// we won't act on any further items
|
||||
return;
|
||||
}
|
||||
|
||||
if (childObservable == null) {
|
||||
throw new IllegalArgumentException("Observable<T> can not be null.");
|
||||
}
|
||||
|
||||
/**
|
||||
* For each child Observable we receive we'll subscribe with a separate Observer
|
||||
* that will each then forward their sequences to the actualObserver.
|
||||
* <p>
|
||||
* We use separate child Observers for each sequence to simplify the onComplete/onError handling so each sequence has its own lifecycle.
|
||||
*/
|
||||
ChildObserver _w = new ChildObserver(actualObserver);
|
||||
childObservers.put(_w, _w);
|
||||
Subscription _subscription = childObservable.subscribe(_w);
|
||||
// remember this Observer and the subscription from it
|
||||
childSubscriptions.put(_w, _subscription);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to each child Observable<T> and forward their sequence of data to the actualObserver
|
||||
*
|
||||
*/
|
||||
private class ChildObserver implements Observer<T> {
|
||||
|
||||
private final Observer<T> actualObserver;
|
||||
private volatile boolean finished = false;
|
||||
|
||||
public ChildObserver(Observer<T> actualObserver) {
|
||||
this.actualObserver = actualObserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// remove self from map of Observers
|
||||
childObservers.remove(this);
|
||||
// if there are now 0 Observers left, so if the parent is also completed we send the onComplete to the actualObserver
|
||||
// if the parent is not complete that means there is another sequence (and child Observer) to come
|
||||
if (!stopped.get()) {
|
||||
finishObserver();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
if (!stopped.get()) {
|
||||
onErrorReceived.add(e);
|
||||
// mark this ChildObserver as done
|
||||
childObservers.remove(this);
|
||||
// but do NOT forward to actualObserver as we want other ChildObservers to continue until completion
|
||||
// and we'll delay the sending of onError until all others are done
|
||||
|
||||
// we mark finished==true as a safety to ensure that if further calls to onNext occur we ignore them
|
||||
finished = true;
|
||||
|
||||
// check for whether the parent is completed and if so then perform the 'finishing' actions
|
||||
finishObserver();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onComplete and onError when called need to check for the parent being complete and if so send the onCompleted or onError to the actualObserver.
|
||||
* <p>
|
||||
* This does NOT get invoked if synchronous execution occurs, but will when asynchronously executing.
|
||||
* <p>
|
||||
* TestCase testErrorDelayed4WithThreading specifically tests this use case.
|
||||
*/
|
||||
private void finishObserver() {
|
||||
if (childObservers.size() == 0 && parentCompleted) {
|
||||
if (ourSubscription.stop()) {
|
||||
// this thread 'won' the race to unsubscribe/stop so let's send onError or onCompleted
|
||||
if (onErrorReceived.size() == 1) {
|
||||
// an onError was received from 1 ChildObserver so we now send it as a delayed error
|
||||
actualObserver.onError(onErrorReceived.peek());
|
||||
} else if (onErrorReceived.size() > 1) {
|
||||
// an onError was received from more than 1 ChildObserver so we now send it as a delayed error
|
||||
actualObserver.onError(new CompositeException(onErrorReceived));
|
||||
} else {
|
||||
// no delayed error so send onCompleted
|
||||
actualObserver.onCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
// in case the Observable is poorly behaved and doesn't listen to the unsubscribe request
|
||||
// we'll ignore anything that comes in after we've unsubscribed or an onError has been received and delayed
|
||||
if (!stopped.get() && !finished) {
|
||||
actualObserver.onNext(args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
106
HMCLAPI/src/main/java/rx/operators/OperationMostRecent.java
Normal file
106
HMCLAPI/src/main/java/rx/operators/OperationMostRecent.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* 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.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.util.Exceptions;
|
||||
|
||||
/**
|
||||
* Samples the most recent value in an observable sequence.
|
||||
*/
|
||||
public final class OperationMostRecent {
|
||||
|
||||
public static <T> Iterable<T> mostRecent(final Observable<T> source, T initialValue) {
|
||||
|
||||
MostRecentObserver<T> mostRecentObserver = new MostRecentObserver<>(initialValue);
|
||||
MostRecentIterator<T> nextIterator = new MostRecentIterator<>(mostRecentObserver);
|
||||
|
||||
source.subscribe(mostRecentObserver);
|
||||
|
||||
return () -> nextIterator;
|
||||
|
||||
}
|
||||
|
||||
private static class MostRecentIterator<T> implements Iterator<T> {
|
||||
|
||||
private final MostRecentObserver<T> observer;
|
||||
|
||||
private MostRecentIterator(MostRecentObserver<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !observer.isCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (observer.getException() != null) {
|
||||
throw Exceptions.propagate(observer.getException());
|
||||
}
|
||||
return observer.getRecentValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Read only iterator");
|
||||
}
|
||||
}
|
||||
|
||||
private static class MostRecentObserver<T> implements Observer<T> {
|
||||
private final AtomicBoolean completed = new AtomicBoolean(false);
|
||||
private final AtomicReference<T> value;
|
||||
private final AtomicReference<Exception> exception = new AtomicReference<>();
|
||||
|
||||
private MostRecentObserver(T value) {
|
||||
this.value = new AtomicReference<>(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
completed.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
exception.set(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
value.set(args);
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return completed.get();
|
||||
}
|
||||
|
||||
public Exception getException() {
|
||||
return exception.get();
|
||||
}
|
||||
|
||||
public T getRecentValue() {
|
||||
return value.get();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
148
HMCLAPI/src/main/java/rx/operators/OperationNext.java
Normal file
148
HMCLAPI/src/main/java/rx/operators/OperationNext.java
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
44
HMCLAPI/src/main/java/rx/operators/OperationObserveOn.java
Normal file
44
HMCLAPI/src/main/java/rx/operators/OperationObserveOn.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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 rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Scheduler;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public class OperationObserveOn {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> observeOn(Observable<T> source, Scheduler scheduler) {
|
||||
return new ObserveOn<>(source, scheduler);
|
||||
}
|
||||
|
||||
private static class ObserveOn<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final Observable<T> source;
|
||||
private final Scheduler scheduler;
|
||||
|
||||
public ObserveOn(Observable<T> source, Scheduler scheduler) {
|
||||
this.source = source;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
return source.subscribe(new ScheduledObserver<>(observer, scheduler));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.CompositeException;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationOnErrorResumeNextViaFunction<T> {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> onErrorResumeNextViaFunction(Observable<T> originalSequence, Func1<Exception, Observable<T>> resumeFunction) {
|
||||
return new OnErrorResumeNextViaFunction<>(originalSequence, resumeFunction);
|
||||
}
|
||||
|
||||
private static class OnErrorResumeNextViaFunction<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Func1<Exception, Observable<T>> resumeFunction;
|
||||
private final Observable<T> originalSequence;
|
||||
|
||||
public OnErrorResumeNextViaFunction(Observable<T> originalSequence, Func1<Exception, Observable<T>> resumeFunction) {
|
||||
this.resumeFunction = resumeFunction;
|
||||
this.originalSequence = originalSequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
// AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed
|
||||
final AtomicReference<AtomicObservableSubscription> subscriptionRef = new AtomicReference<>(new AtomicObservableSubscription());
|
||||
|
||||
// subscribe to the original Observable and remember the subscription
|
||||
subscriptionRef.get().wrap(new AtomicObservableSubscription(originalSequence.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
// forward the successful calls
|
||||
observer.onNext(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of passing the onError forward, we intercept and "resume" with the resumeSequence.
|
||||
*/
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
/* remember what the current subscription is so we can determine if someone unsubscribes concurrently */
|
||||
AtomicObservableSubscription currentSubscription = subscriptionRef.get();
|
||||
// check that we have not been unsubscribed before we can process the error
|
||||
if (currentSubscription != null) {
|
||||
try {
|
||||
Observable<T> resumeSequence = resumeFunction.call(ex);
|
||||
/* error occurred, so switch subscription to the 'resumeSequence' */
|
||||
AtomicObservableSubscription innerSubscription = new AtomicObservableSubscription(resumeSequence.subscribe(observer));
|
||||
/* we changed the sequence, so also change the subscription to the one of the 'resumeSequence' instead */
|
||||
if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription)) {
|
||||
// we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below
|
||||
// so we want to immediately unsubscribe from the resumeSequence we just subscribed to
|
||||
innerSubscription.unsubscribe();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// the resume function failed so we need to call onError
|
||||
// I am using CompositeException so that both exceptions can be seen
|
||||
observer.onError(new CompositeException("OnErrorResume function failed", Arrays.asList(ex, e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// forward the successful calls
|
||||
observer.onCompleted();
|
||||
}
|
||||
})));
|
||||
|
||||
return () -> {
|
||||
// this will get either the original, or the resumeSequence one and unsubscribe on it
|
||||
Subscription s = subscriptionRef.getAndSet(null);
|
||||
if (s != null)
|
||||
s.unsubscribe();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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.concurrent.atomic.AtomicReference;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationOnErrorResumeNextViaObservable<T> {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> onErrorResumeNextViaObservable(Observable<T> originalSequence, Observable<T> resumeSequence) {
|
||||
return new OnErrorResumeNextViaObservable<>(originalSequence, resumeSequence);
|
||||
}
|
||||
|
||||
private static class OnErrorResumeNextViaObservable<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private final Observable<T> resumeSequence;
|
||||
private final Observable<T> originalSequence;
|
||||
|
||||
public OnErrorResumeNextViaObservable(Observable<T> originalSequence, Observable<T> resumeSequence) {
|
||||
this.resumeSequence = resumeSequence;
|
||||
this.originalSequence = originalSequence;
|
||||
}
|
||||
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
// AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed
|
||||
final AtomicReference<AtomicObservableSubscription> subscriptionRef = new AtomicReference<>(subscription);
|
||||
|
||||
// subscribe to the original Observable and remember the subscription
|
||||
subscription.wrap(originalSequence.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
// forward the successful calls
|
||||
observer.onNext(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of passing the onError forward, we intercept and "resume" with the resumeSequence.
|
||||
*/
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
/* remember what the current subscription is so we can determine if someone unsubscribes concurrently */
|
||||
AtomicObservableSubscription currentSubscription = subscriptionRef.get();
|
||||
// check that we have not been unsubscribed before we can process the error
|
||||
if (currentSubscription != null) {
|
||||
/* error occurred, so switch subscription to the 'resumeSequence' */
|
||||
AtomicObservableSubscription innerSubscription = new AtomicObservableSubscription(resumeSequence.subscribe(observer));
|
||||
/* we changed the sequence, so also change the subscription to the one of the 'resumeSequence' instead */
|
||||
if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription)) {
|
||||
// we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below
|
||||
// so we want to immediately unsubscribe from the resumeSequence we just subscribed to
|
||||
innerSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// forward the successful calls
|
||||
observer.onCompleted();
|
||||
}
|
||||
}));
|
||||
|
||||
return () -> {
|
||||
// this will get either the original, or the resumeSequence one and unsubscribe on it
|
||||
Subscription s = subscriptionRef.getAndSet(null);
|
||||
if (s != null)
|
||||
s.unsubscribe();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
108
HMCLAPI/src/main/java/rx/operators/OperationOnErrorReturn.java
Normal file
108
HMCLAPI/src/main/java/rx/operators/OperationOnErrorReturn.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 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.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.CompositeException;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* When an onError occurs the resumeFunction will be executed and it's response passed to onNext instead of calling onError.
|
||||
*/
|
||||
public final class OperationOnErrorReturn<T> {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> onErrorReturn(Observable<T> originalSequence, Func1<Exception, T> resumeFunction) {
|
||||
return new OnErrorReturn<>(originalSequence, resumeFunction);
|
||||
}
|
||||
|
||||
private static class OnErrorReturn<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final Func1<Exception, T> resumeFunction;
|
||||
private final Observable<T> originalSequence;
|
||||
|
||||
public OnErrorReturn(Observable<T> originalSequence, Func1<Exception, T> resumeFunction) {
|
||||
this.resumeFunction = resumeFunction;
|
||||
this.originalSequence = originalSequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
// AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed
|
||||
final AtomicReference<AtomicObservableSubscription> subscriptionRef = new AtomicReference<>(subscription);
|
||||
|
||||
// subscribe to the original Observable and remember the subscription
|
||||
subscription.wrap(originalSequence.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
// forward the successful calls
|
||||
observer.onNext(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of passing the onError forward, we intercept and "resume" with the resumeSequence.
|
||||
*/
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
/* remember what the current subscription is so we can determine if someone unsubscribes concurrently */
|
||||
AtomicObservableSubscription currentSubscription = subscriptionRef.get();
|
||||
// check that we have not been unsubscribed before we can process the error
|
||||
if (currentSubscription != null) {
|
||||
try {
|
||||
/* error occurred, so execute the function, give it the exception and call onNext with the response */
|
||||
onNext(resumeFunction.call(ex));
|
||||
/*
|
||||
* we are not handling an exception thrown from this function ... should we do something?
|
||||
* error handling within an error handler is a weird one to determine what we should do
|
||||
* right now I'm going to just let it throw whatever exceptions occur (such as NPE)
|
||||
* but I'm considering calling the original Observer.onError to act as if this OnErrorReturn operator didn't happen
|
||||
*/
|
||||
|
||||
/* we are now completed */
|
||||
onCompleted();
|
||||
|
||||
/* unsubscribe since it blew up */
|
||||
currentSubscription.unsubscribe();
|
||||
} catch (Exception e) {
|
||||
// the return function failed so we need to call onError
|
||||
// I am using CompositeException so that both exceptions can be seen
|
||||
observer.onError(new CompositeException("OnErrorReturn function failed", Arrays.asList(ex, e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// forward the successful calls
|
||||
observer.onCompleted();
|
||||
}
|
||||
}));
|
||||
|
||||
return () -> {
|
||||
// this will get either the original, or the resumeSequence one and unsubscribe on it
|
||||
Subscription s = subscriptionRef.getAndSet(null);
|
||||
if (s != null)
|
||||
s.unsubscribe();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
130
HMCLAPI/src/main/java/rx/operators/OperationScan.java
Normal file
130
HMCLAPI/src/main/java/rx/operators/OperationScan.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* 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 rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
import rx.util.functions.Func2;
|
||||
|
||||
public final class OperationScan {
|
||||
/**
|
||||
* Applies an accumulator function over an observable sequence and returns each intermediate result with the specified source and accumulator.
|
||||
*
|
||||
* @param sequence
|
||||
* An observable sequence of elements to project.
|
||||
* @param initialValue
|
||||
* The initial (seed) accumulator value.
|
||||
* @param accumulator
|
||||
* An accumulator function to be invoked on each element from the sequence.
|
||||
*
|
||||
* @return An observable sequence whose elements are the result of accumulating the output from the list of Observables.
|
||||
* @see http://msdn.microsoft.com/en-us/library/hh211665(v=vs.103).aspx
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> scan(Observable<T> sequence, T initialValue, Func2<T, T, T> accumulator) {
|
||||
return new Accumulator<>(sequence, initialValue, accumulator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies an accumulator function over an observable sequence and returns each intermediate result with the specified source and accumulator.
|
||||
*
|
||||
* @param sequence
|
||||
* An observable sequence of elements to project.
|
||||
* @param accumulator
|
||||
* An accumulator function to be invoked on each element from the sequence.
|
||||
*
|
||||
* @return An observable sequence whose elements are the result of accumulating the output from the list of Observables.
|
||||
* @see http://msdn.microsoft.com/en-us/library/hh211665(v=vs.103).aspx
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> scan(Observable<T> sequence, Func2<T, T, T> accumulator) {
|
||||
return new Accumulator<>(sequence, null, accumulator);
|
||||
}
|
||||
|
||||
private static class Accumulator<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final Observable<T> sequence;
|
||||
private final T initialValue;
|
||||
private final Func2<T, T, T> accumlatorFunction;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
private Accumulator(Observable<T> sequence, T initialValue, Func2<T, T, T> accumulator) {
|
||||
this.sequence = sequence;
|
||||
this.initialValue = initialValue;
|
||||
this.accumlatorFunction = accumulator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
|
||||
return subscription.wrap(sequence.subscribe(new Observer<T>() {
|
||||
private T acc = initialValue;
|
||||
private boolean hasSentInitialValue = false;
|
||||
|
||||
/**
|
||||
* We must synchronize this because we can't allow
|
||||
* multiple threads to execute the 'accumulatorFunction' at the same time because
|
||||
* the accumulator code very often will be doing mutation of the 'acc' object such as a non-threadsafe HashMap
|
||||
*
|
||||
* Because it's synchronized it's using non-atomic variables since everything in this method is single-threaded
|
||||
*/
|
||||
@Override
|
||||
public synchronized void onNext(T value) {
|
||||
if (acc == null) {
|
||||
// we assume that acc is not allowed to be returned from accumulatorValue
|
||||
// so it's okay to check null as being the state we initialize on
|
||||
acc = value;
|
||||
// this is all we do for this first value if we didn't have an initialValue
|
||||
return;
|
||||
}
|
||||
if (!hasSentInitialValue) {
|
||||
hasSentInitialValue = true;
|
||||
observer.onNext(acc);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
acc = accumlatorFunction.call(acc, value);
|
||||
if (acc == null) {
|
||||
onError(new IllegalArgumentException("Null is an unsupported return value for an accumulator."));
|
||||
return;
|
||||
}
|
||||
observer.onNext(acc);
|
||||
} catch (Exception ex) {
|
||||
observer.onError(ex);
|
||||
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
|
||||
// synchronized because we access 'hasSentInitialValue'
|
||||
@Override
|
||||
public synchronized void onCompleted() {
|
||||
// if only one sequence value existed, we send it without any accumulation
|
||||
if (!hasSentInitialValue) {
|
||||
observer.onNext(acc);
|
||||
}
|
||||
observer.onCompleted();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
98
HMCLAPI/src/main/java/rx/operators/OperationSkip.java
Normal file
98
HMCLAPI/src/main/java/rx/operators/OperationSkip.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 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.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Skips a specified number of contiguous values from the start of a Observable sequence and then returns the remaining values.
|
||||
*/
|
||||
public final class OperationSkip {
|
||||
|
||||
/**
|
||||
* Skips a specified number of contiguous values from the start of a Observable sequence and then returns the remaining values.
|
||||
*
|
||||
* @param items
|
||||
* @param num
|
||||
* @return
|
||||
*
|
||||
* @see http://msdn.microsoft.com/en-us/library/hh229847(v=vs.103).aspx
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> skip(final Observable<T> items, final int num) {
|
||||
// wrap in a Observable so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
|
||||
return new Skip<>(items, num)::call;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
|
||||
* <p>
|
||||
* It IS thread-safe from within it while receiving onNext events from multiple threads.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private static class Skip<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final int num;
|
||||
private final Observable<T> items;
|
||||
|
||||
Skip(final Observable<T> items, final int num) {
|
||||
this.num = num;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return items.subscribe(new ItemObserver(observer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to subscribe to the 'items' Observable sequence and forward to the actualObserver up to 'num' count.
|
||||
*/
|
||||
private class ItemObserver implements Observer<T> {
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger();
|
||||
private final Observer<T> observer;
|
||||
|
||||
public ItemObserver(Observer<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
// skip them until we reach the 'num' value
|
||||
if (counter.incrementAndGet() > num) {
|
||||
observer.onNext(args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
67
HMCLAPI/src/main/java/rx/operators/OperationSubscribeOn.java
Normal file
67
HMCLAPI/src/main/java/rx/operators/OperationSubscribeOn.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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 rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Scheduler;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Action0;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
|
||||
public class OperationSubscribeOn {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> subscribeOn(Observable<T> source, Scheduler scheduler) {
|
||||
return new SubscribeOn<>(source, scheduler);
|
||||
}
|
||||
|
||||
private static class SubscribeOn<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final Observable<T> source;
|
||||
private final Scheduler scheduler;
|
||||
|
||||
public SubscribeOn(Observable<T> source, Scheduler scheduler) {
|
||||
this.source = source;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<T> observer) {
|
||||
return scheduler.schedule(() -> new ScheduledSubscription(source.subscribe(observer), scheduler));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ScheduledSubscription implements Subscription {
|
||||
private final Subscription underlying;
|
||||
private final Scheduler scheduler;
|
||||
|
||||
private ScheduledSubscription(Subscription underlying, Scheduler scheduler) {
|
||||
this.underlying = underlying;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
scheduler.schedule(new Action0() {
|
||||
@Override
|
||||
public void call() {
|
||||
underlying.unsubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
72
HMCLAPI/src/main/java/rx/operators/OperationSynchronize.java
Normal file
72
HMCLAPI/src/main/java/rx/operators/OperationSynchronize.java
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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 rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.SynchronizedObserver;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* An observable that wraps an observable of the same type and then enforces the semantics
|
||||
* expected of a well-behaved observable.
|
||||
* <p>
|
||||
* An observable that ensures onNext, onCompleted, or onError calls on its subscribers are
|
||||
* not interleaved, onCompleted and onError are only called once respectively, and no
|
||||
* onNext calls follow onCompleted and onError calls.
|
||||
* <p>
|
||||
* NOTE: {@link Observable#create} already wraps Observables so this is generally redundant.
|
||||
*
|
||||
* @param <T>
|
||||
* The type of the observable sequence.
|
||||
*/
|
||||
public final class OperationSynchronize<T> {
|
||||
|
||||
/**
|
||||
* Accepts an observable and wraps it in another observable which ensures that the resulting observable is well-behaved.
|
||||
*
|
||||
* A well-behaved observable ensures onNext, onCompleted, or onError calls to its subscribers are
|
||||
* not interleaved, onCompleted and onError are only called once respectively, and no
|
||||
* onNext calls follow onCompleted and onError calls.
|
||||
*
|
||||
* @param observable
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> synchronize(Observable<T> observable) {
|
||||
return new Synchronize<>(observable);
|
||||
}
|
||||
|
||||
private static class Synchronize<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
public Synchronize(Observable<T> innerObservable) {
|
||||
this.innerObservable = innerObservable;
|
||||
}
|
||||
|
||||
private final Observable<T> innerObservable;
|
||||
private SynchronizedObserver<T> atomicObserver;
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
atomicObserver = new SynchronizedObserver<>(observer, subscription);
|
||||
return subscription.wrap(innerObservable.subscribe(atomicObserver));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
132
HMCLAPI/src/main/java/rx/operators/OperationTake.java
Normal file
132
HMCLAPI/src/main/java/rx/operators/OperationTake.java
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* 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.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.subscriptions.Subscriptions;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Returns a specified number of contiguous values from the start of an observable sequence.
|
||||
*/
|
||||
public final class OperationTake {
|
||||
|
||||
/**
|
||||
* Returns a specified number of contiguous values from the start of an observable sequence.
|
||||
*
|
||||
* @param items
|
||||
* @param num
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> take(final Observable<T> items, final int num) {
|
||||
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
|
||||
return new Take<>(items, num)::call;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
|
||||
* <p>
|
||||
* It IS thread-safe from within it while receiving onNext events from multiple threads.
|
||||
* <p>
|
||||
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above.
|
||||
* <p>
|
||||
* Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private static class Take<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final AtomicInteger counter = new AtomicInteger();
|
||||
private final Observable<T> items;
|
||||
private final int num;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
private Take(Observable<T> items, int num) {
|
||||
this.items = items;
|
||||
this.num = num;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
if (num < 1) {
|
||||
items.subscribe(new Observer<T>()
|
||||
{
|
||||
@Override
|
||||
public void onCompleted()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args)
|
||||
{
|
||||
}
|
||||
}).unsubscribe();
|
||||
observer.onCompleted();
|
||||
return Subscriptions.empty();
|
||||
}
|
||||
|
||||
return subscription.wrap(items.subscribe(new ItemObserver(observer)));
|
||||
}
|
||||
|
||||
private class ItemObserver implements Observer<T> {
|
||||
private final Observer<T> observer;
|
||||
|
||||
public ItemObserver(Observer<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (counter.getAndSet(num) < num) {
|
||||
observer.onCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
if (counter.getAndSet(num) < num) {
|
||||
observer.onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
final int count = counter.incrementAndGet();
|
||||
if (count <= num) {
|
||||
observer.onNext(args);
|
||||
if (count == num) {
|
||||
observer.onCompleted();
|
||||
}
|
||||
}
|
||||
if (count >= num) {
|
||||
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
84
HMCLAPI/src/main/java/rx/operators/OperationTakeLast.java
Normal file
84
HMCLAPI/src/main/java/rx/operators/OperationTakeLast.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 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.LinkedBlockingDeque;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Returns a specified number of contiguous elements from the end of an observable sequence.
|
||||
*/
|
||||
public final class OperationTakeLast {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> takeLast(final Observable<T> items, final int count) {
|
||||
return new TakeLast<>(items, count)::call;
|
||||
}
|
||||
|
||||
private static class TakeLast<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final int count;
|
||||
private final Observable<T> items;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
TakeLast(final Observable<T> items, final int count) {
|
||||
this.count = count;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return subscription.wrap(items.subscribe(new ItemObserver(observer)));
|
||||
}
|
||||
|
||||
private class ItemObserver implements Observer<T> {
|
||||
|
||||
private LinkedBlockingDeque<T> deque = new LinkedBlockingDeque<>(count);
|
||||
private final Observer<T> observer;
|
||||
|
||||
public ItemObserver(Observer<T> observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
Iterator<T> reverse = deque.descendingIterator();
|
||||
while (reverse.hasNext()) {
|
||||
observer.onNext(reverse.next());
|
||||
}
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
while (!deque.offerFirst(args)) {
|
||||
deque.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
131
HMCLAPI/src/main/java/rx/operators/OperationTakeWhile.java
Normal file
131
HMCLAPI/src/main/java/rx/operators/OperationTakeWhile.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* 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.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.AtomicObserver;
|
||||
import rx.util.functions.Func1;
|
||||
import rx.util.functions.Func2;
|
||||
|
||||
/**
|
||||
* Returns values from an observable sequence as long as a specified condition is true, and then skips the remaining values.
|
||||
*/
|
||||
public final class OperationTakeWhile {
|
||||
|
||||
/**
|
||||
* Returns a specified number of contiguous values from the start of an observable sequence.
|
||||
*
|
||||
* @param items
|
||||
* @param predicate
|
||||
* a function to test each source element for a condition
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> takeWhile(final Observable<T> items, final Func1<T, Boolean> predicate) {
|
||||
return takeWhileWithIndex(items, OperationTakeWhile.<T> skipIndex(predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns values from an observable sequence as long as a specified condition is true, and then skips the remaining values.
|
||||
*
|
||||
* @param items
|
||||
* @param predicate
|
||||
* a function to test each element for a condition; the second parameter of the function represents the index of the source element; otherwise, false.
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<T>, Subscription> takeWhileWithIndex(final Observable<T> items, final Func2<T, Integer, Boolean> predicate) {
|
||||
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
|
||||
return new TakeWhile<T>(items, predicate)::call;
|
||||
}
|
||||
|
||||
private static <T> Func2<T, Integer, Boolean> skipIndex(final Func1<T, Boolean> underlying) {
|
||||
return (T input, Integer index) -> underlying.call(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
|
||||
* <p>
|
||||
* It IS thread-safe from within it while receiving onNext events from multiple threads.
|
||||
* <p>
|
||||
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above.
|
||||
* <p>
|
||||
* Note how the takeWhileWithIndex() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private static class TakeWhile<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final AtomicInteger counter = new AtomicInteger();
|
||||
private final Observable<T> items;
|
||||
private final Func2<T, Integer, Boolean> predicate;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
private TakeWhile(Observable<T> items, Func2<T, Integer, Boolean> predicate) {
|
||||
this.items = items;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
return subscription.wrap(items.subscribe(new ItemObserver(observer)));
|
||||
}
|
||||
|
||||
private class ItemObserver implements Observer<T> {
|
||||
private final Observer<T> observer;
|
||||
|
||||
public ItemObserver(Observer<T> observer) {
|
||||
// Using AtomicObserver because the unsubscribe, onCompleted, onError and error handling behavior
|
||||
// needs "isFinished" logic to not send duplicated events
|
||||
// The 'testTakeWhile1' and 'testTakeWhile2' tests fail without this.
|
||||
this.observer = new AtomicObserver<>(subscription, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
Boolean isSelected;
|
||||
try {
|
||||
isSelected = predicate.call(args, counter.getAndIncrement());
|
||||
} catch (Exception e) {
|
||||
observer.onError(e);
|
||||
return;
|
||||
}
|
||||
if (isSelected) {
|
||||
observer.onNext(args);
|
||||
} else {
|
||||
observer.onCompleted();
|
||||
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.subscriptions.Subscriptions;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public class OperationToObservableFuture {
|
||||
private static class ToObservableFuture<T> implements Func1<Observer<T>, Subscription> {
|
||||
private final Future<T> that;
|
||||
private final Long time;
|
||||
private final TimeUnit unit;
|
||||
|
||||
public ToObservableFuture(Future<T> that) {
|
||||
this.that = that;
|
||||
this.time = null;
|
||||
this.unit = null;
|
||||
}
|
||||
|
||||
public ToObservableFuture(Future<T> that, long time, TimeUnit unit) {
|
||||
this.that = that;
|
||||
this.time = time;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
try {
|
||||
T value = (time == null) ? that.get() : that.get(time, unit);
|
||||
|
||||
if (!that.isCancelled()) {
|
||||
observer.onNext(value);
|
||||
}
|
||||
observer.onCompleted();
|
||||
} catch (Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
// the get() has already completed so there is no point in
|
||||
// giving the user a way to cancel.
|
||||
return Subscriptions.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> toObservableFuture(final Future<T> that) {
|
||||
return new ToObservableFuture<>(that);
|
||||
}
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> toObservableFuture(final Future<T> that, long time, TimeUnit unit) {
|
||||
return new ToObservableFuture<>(that, time, unit);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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 rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.subscriptions.Subscriptions;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Accepts an Iterable object and exposes it as an Observable.
|
||||
*
|
||||
* @param <T>
|
||||
* The type of the Iterable sequence.
|
||||
*/
|
||||
public final class OperationToObservableIterable<T> {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> toObservableIterable(Iterable<T> iterable) {
|
||||
return (Observer<T> observer) -> {
|
||||
for (T item : iterable)
|
||||
observer.onNext(item);
|
||||
observer.onCompleted();
|
||||
|
||||
return Subscriptions.empty();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationToObservableList<T> {
|
||||
|
||||
public static <T> Func1<Observer<List<T>>, Subscription> toObservableList(Observable<T> that) {
|
||||
return new ToObservableList<>(that);
|
||||
}
|
||||
|
||||
private static class ToObservableList<T> implements Func1<Observer<List<T>>, Subscription> {
|
||||
|
||||
private final Observable<T> that;
|
||||
|
||||
public ToObservableList(Observable<T> that) {
|
||||
this.that = that;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<List<T>> observer) {
|
||||
|
||||
return that.subscribe(new Observer<T>() {
|
||||
final ConcurrentLinkedQueue<T> list = new ConcurrentLinkedQueue<>();
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
// onNext can be concurrently executed so list must be thread-safe
|
||||
list.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
try {
|
||||
// copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface
|
||||
ArrayList<T> l = new ArrayList<>(list.size());
|
||||
for (T t : list)
|
||||
l.add(t);
|
||||
|
||||
// benjchristensen => I want to make this list immutable but some clients are sorting this
|
||||
// instead of using toSortedList() and this change breaks them until we migrate their code.
|
||||
// observer.onNext(Collections.unmodifiableList(l));
|
||||
observer.onNext(l);
|
||||
observer.onCompleted();
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* 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.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
import rx.util.functions.Func2;
|
||||
|
||||
/**
|
||||
* Similar to toList in that it converts a sequence<T> into a List<T> except that it accepts a Function that will provide an implementation of Comparator.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public final class OperationToObservableSortedList<T> {
|
||||
|
||||
/**
|
||||
* Sort T objects by their natural order (object must implement Comparable).
|
||||
*
|
||||
* @param sequence
|
||||
* @throws ClassCastException
|
||||
* if T objects do not implement Comparable
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<List<T>>, Subscription> toSortedList(Observable<T> sequence) {
|
||||
return new ToObservableSortedList<>(sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort T objects using the defined sort function.
|
||||
*
|
||||
* @param sequence
|
||||
* @param sortFunction
|
||||
* @return
|
||||
*/
|
||||
public static <T> Func1<Observer<List<T>>, Subscription> toSortedList(Observable<T> sequence, Func2<T, T, Integer> sortFunction) {
|
||||
return new ToObservableSortedList<>(sequence, sortFunction);
|
||||
}
|
||||
|
||||
private static class ToObservableSortedList<T> implements Func1<Observer<List<T>>, Subscription> {
|
||||
|
||||
private final Observable<T> that;
|
||||
private final ConcurrentLinkedQueue<T> list = new ConcurrentLinkedQueue<>();
|
||||
private final Func2<T, T, Integer> sortFunction;
|
||||
|
||||
// unchecked as we're support Object for the default
|
||||
@SuppressWarnings("unchecked")
|
||||
private ToObservableSortedList(Observable<T> that) {
|
||||
this(that, defaultSortFunction);
|
||||
}
|
||||
|
||||
private ToObservableSortedList(Observable<T> that, Func2<T, T, Integer> sortFunction) {
|
||||
this.that = that;
|
||||
this.sortFunction = sortFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<List<T>> observer) {
|
||||
return that.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onNext(T value) {
|
||||
// onNext can be concurrently executed so list must be thread-safe
|
||||
list.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
observer.onError(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
try {
|
||||
// copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface
|
||||
ArrayList<T> l = new ArrayList<>(list.size());
|
||||
for (T t : list) {
|
||||
l.add(t);
|
||||
}
|
||||
|
||||
// sort the list before delivery
|
||||
Collections.sort(l, (T o1, T o2) -> sortFunction.call(o1, o2));
|
||||
|
||||
observer.onNext(Collections.unmodifiableList(l));
|
||||
observer.onCompleted();
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// raw because we want to support Object for this default
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static Func2 defaultSortFunction = new DefaultComparableFunction();
|
||||
|
||||
private static class DefaultComparableFunction implements Func2<Object, Object, Integer> {
|
||||
|
||||
// unchecked because we want to support Object for this default
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Integer call(Object t1, Object t2) {
|
||||
Comparable<Object> c1 = (Comparable<Object>) t1;
|
||||
Comparable<Object> c2 = (Comparable<Object>) t2;
|
||||
return c1.compareTo(c2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
28
HMCLAPI/src/main/java/rx/operators/OperationWhere.java
Normal file
28
HMCLAPI/src/main/java/rx/operators/OperationWhere.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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 rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public final class OperationWhere {
|
||||
|
||||
public static <T> Func1<Observer<T>, Subscription> where(Observable<T> that, Func1<T, Boolean> predicate) {
|
||||
return OperationFilter.filter(that, predicate);
|
||||
}
|
||||
}
|
||||
259
HMCLAPI/src/main/java/rx/operators/OperationZip.java
Normal file
259
HMCLAPI/src/main/java/rx/operators/OperationZip.java
Normal file
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* 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.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.SynchronizedObserver;
|
||||
import rx.util.functions.Func1;
|
||||
import rx.util.functions.Func2;
|
||||
import rx.util.functions.Func3;
|
||||
import rx.util.functions.Func4;
|
||||
import rx.util.functions.FuncN;
|
||||
import rx.util.functions.Functions;
|
||||
|
||||
public final class OperationZip {
|
||||
|
||||
public static <T0, T1, R> Func1<Observer<R>, Subscription> zip(Observable<T0> w0, Observable<T1> w1, Func2<T0, T1, R> zipFunction) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(zipFunction));
|
||||
a.addObserver(new ZipObserver<>(a, w0));
|
||||
a.addObserver(new ZipObserver<>(a, w1));
|
||||
return a;
|
||||
}
|
||||
|
||||
public static <T0, T1, T2, R> Func1<Observer<R>, Subscription> zip(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Func3<T0, T1, T2, R> zipFunction) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(zipFunction));
|
||||
a.addObserver(new ZipObserver<>(a, w0));
|
||||
a.addObserver(new ZipObserver<>(a, w1));
|
||||
a.addObserver(new ZipObserver<>(a, w2));
|
||||
return a;
|
||||
}
|
||||
|
||||
public static <T0, T1, T2, T3, R> Func1<Observer<R>, Subscription> zip(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Observable<T3> w3, Func4<T0, T1, T2, T3, R> zipFunction) {
|
||||
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(zipFunction));
|
||||
a.addObserver(new ZipObserver<>(a, w0));
|
||||
a.addObserver(new ZipObserver<>(a, w1));
|
||||
a.addObserver(new ZipObserver<>(a, w2));
|
||||
a.addObserver(new ZipObserver<>(a, w3));
|
||||
return a;
|
||||
}
|
||||
|
||||
/*
|
||||
* ThreadSafe
|
||||
*/
|
||||
private static class ZipObserver<R, T> implements Observer<T> {
|
||||
final Observable<T> w;
|
||||
final Aggregator<R> a;
|
||||
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
private final AtomicBoolean subscribed = new AtomicBoolean(false);
|
||||
|
||||
public ZipObserver(Aggregator<R> a, Observable<T> w) {
|
||||
this.a = a;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
public void startWatching() {
|
||||
if (subscribed.compareAndSet(false, true)) {
|
||||
// only subscribe once even if called more than once
|
||||
subscription.wrap(w.subscribe(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
a.complete(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
a.error(this, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
try {
|
||||
a.next(this, args);
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive notifications from each of the Observables we are reducing and execute the zipFunction whenever we have received events from all Observables.
|
||||
*
|
||||
* This class is thread-safe.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
private static class Aggregator<T> implements Func1<Observer<T>, Subscription> {
|
||||
|
||||
private volatile SynchronizedObserver<T> observer;
|
||||
private final FuncN<T> zipFunction;
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
private final AtomicBoolean running = new AtomicBoolean(true);
|
||||
private final ConcurrentHashMap<ZipObserver<T, ?>, Boolean> completed = new ConcurrentHashMap<>();
|
||||
|
||||
/* we use ConcurrentHashMap despite synchronization of methods because stop() does NOT use synchronization and this map is used by it and can be called by other threads */
|
||||
private final ConcurrentHashMap<ZipObserver<T, ?>, ConcurrentLinkedQueue<Object>> receivedValuesPerObserver = new ConcurrentHashMap<>();
|
||||
/* we use a ConcurrentLinkedQueue to retain ordering (I'd like to just use a ConcurrentLinkedHashMap for 'receivedValuesPerObserver' but that doesn't exist in standard java */
|
||||
private final ConcurrentLinkedQueue<ZipObserver<T, ?>> observers = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public Aggregator(FuncN<T> zipFunction) {
|
||||
this.zipFunction = zipFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive notification of a Observer starting (meaning we should require it for aggregation)
|
||||
*
|
||||
* Thread Safety => Invoke ONLY from the static factory methods at top of this class which are always an atomic execution by a single thread.
|
||||
*
|
||||
* @param w
|
||||
*/
|
||||
private void addObserver(ZipObserver<T, ?> w) {
|
||||
// initialize this ZipObserver
|
||||
observers.add(w);
|
||||
receivedValuesPerObserver.put(w, new ConcurrentLinkedQueue<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive notification of a Observer completing its iterations.
|
||||
*
|
||||
* @param w
|
||||
*/
|
||||
void complete(ZipObserver<T, ?> w) {
|
||||
// store that this ZipObserver is completed
|
||||
completed.put(w, Boolean.TRUE);
|
||||
// if all ZipObservers are completed, we mark the whole thing as completed
|
||||
if (completed.size() == observers.size()) {
|
||||
if (running.compareAndSet(true, false)) {
|
||||
// this thread succeeded in setting running=false so let's propagate the completion
|
||||
// mark ourselves as done
|
||||
observer.onCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive error for a Observer. Throw the error up the chain and stop processing.
|
||||
*
|
||||
* @param w
|
||||
*/
|
||||
void error(ZipObserver<T, ?> w, Exception e) {
|
||||
if (running.compareAndSet(true, false)) {
|
||||
// this thread succeeded in setting running=false so let's propagate the error
|
||||
observer.onError(e);
|
||||
/* since we receive an error we want to tell everyone to stop */
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the next value from a Observer.
|
||||
* <p>
|
||||
* If we have received values from all Observers, trigger the zip function, otherwise store the value and keep waiting.
|
||||
*
|
||||
* @param w
|
||||
* @param arg
|
||||
*/
|
||||
void next(ZipObserver<T, ?> w, Object arg) {
|
||||
if (observer == null) {
|
||||
throw new RuntimeException("This shouldn't be running if a Observer isn't registered");
|
||||
}
|
||||
|
||||
/* if we've been 'unsubscribed' don't process anything further even if the things we're watching keep sending (likely because they are not responding to the unsubscribe call) */
|
||||
if (!running.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// store the value we received and below we'll decide if we are to send it to the Observer
|
||||
receivedValuesPerObserver.get(w).add(arg);
|
||||
|
||||
// define here so the variable is out of the synchronized scope
|
||||
Object[] argsToZip = new Object[observers.size()];
|
||||
|
||||
/* we have to synchronize here despite using concurrent data structures because the compound logic here must all be done atomically */
|
||||
synchronized (this) {
|
||||
// if all ZipObservers in 'receivedValues' map have a value, invoke the zipFunction
|
||||
for (ZipObserver<T, ?> rw : receivedValuesPerObserver.keySet()) {
|
||||
if (receivedValuesPerObserver.get(rw).peek() == null) {
|
||||
// we have a null meaning the queues aren't all populated so won't do anything
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if we get to here this means all the queues have data
|
||||
int i = 0;
|
||||
for (ZipObserver<T, ?> rw : observers) {
|
||||
argsToZip[i++] = receivedValuesPerObserver.get(rw).remove();
|
||||
}
|
||||
}
|
||||
// if we did not return above from the synchronized block we can now invoke the zipFunction with all of the args
|
||||
// we do this outside the synchronized block as it is now safe to call this concurrently and don't need to block other threads from calling
|
||||
// this 'next' method while another thread finishes calling this zipFunction
|
||||
observer.onNext(zipFunction.call(argsToZip));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
if (started.compareAndSet(false, true)) {
|
||||
AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
this.observer = new SynchronizedObserver<>(observer, subscription);
|
||||
/* start the Observers */
|
||||
for (ZipObserver<T, ?> rw : observers) {
|
||||
rw.startWatching();
|
||||
}
|
||||
|
||||
return subscription.wrap(this::stop);
|
||||
} else {
|
||||
/* a Observer already has subscribed so blow up */
|
||||
throw new IllegalStateException("Only one Observer can subscribe to this Observable.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do NOT synchronize this because it gets called via unsubscribe which can occur on other threads
|
||||
* and result in deadlocks. (http://jira/browse/API-4060)
|
||||
*
|
||||
* AtomicObservableSubscription uses compareAndSet instead of locking to avoid deadlocks but ensure single-execution.
|
||||
*
|
||||
* We do the same in the implementation of this method.
|
||||
*
|
||||
* ThreadSafety of this method is provided by:
|
||||
* - AtomicBoolean[running].compareAndSet
|
||||
* - ConcurrentLinkedQueue[Observers]
|
||||
* - ZipObserver.subscription being an AtomicObservableSubscription
|
||||
*/
|
||||
private void stop() {
|
||||
/* tell ourselves to stop processing onNext events by setting running=false */
|
||||
if (running.compareAndSet(true, false)) {
|
||||
/* propogate to all Observers to unsubscribe if this thread succeeded in setting running=false */
|
||||
for (ZipObserver<T, ?> o : observers) {
|
||||
if (o.subscription != null) {
|
||||
o.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
116
HMCLAPI/src/main/java/rx/operators/OperatorGroupBy.java
Normal file
116
HMCLAPI/src/main/java/rx/operators/OperatorGroupBy.java
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* 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.concurrent.ConcurrentHashMap;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.observables.GroupedObservable;
|
||||
import rx.util.functions.Func1;
|
||||
import rx.util.functions.Functions;
|
||||
|
||||
public final class OperatorGroupBy {
|
||||
|
||||
public static <K, T, R> Func1<Observer<GroupedObservable<K, R>>, Subscription> groupBy(Observable<T> source, final Func1<T, K> keySelector, final Func1<T, R> elementSelector) {
|
||||
|
||||
final Observable<KeyValue<K, R>> keyval = source.map(new Func1<T, KeyValue<K, R>>() {
|
||||
@Override
|
||||
public KeyValue<K, R> call(T t) {
|
||||
K key = keySelector.call(t);
|
||||
R value = elementSelector.call(t);
|
||||
|
||||
return new KeyValue<K, R>(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
return new GroupBy<K, R>(keyval);
|
||||
}
|
||||
|
||||
public static <K, T> Func1<Observer<GroupedObservable<K, T>>, Subscription> groupBy(Observable<T> source, final Func1<T, K> keySelector) {
|
||||
return groupBy(source, keySelector, Functions.<T> identity());
|
||||
}
|
||||
|
||||
private static class GroupBy<K, V> implements Func1<Observer<GroupedObservable<K, V>>, Subscription> {
|
||||
private final Observable<KeyValue<K, V>> source;
|
||||
private final ConcurrentHashMap<K, Boolean> keys = new ConcurrentHashMap<K, Boolean>();
|
||||
|
||||
private GroupBy(Observable<KeyValue<K, V>> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<GroupedObservable<K, V>> observer) {
|
||||
|
||||
return source.subscribe(new Observer<KeyValue<K, V>>() {
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
observer.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(final KeyValue<K, V> args) {
|
||||
K key = args.key;
|
||||
boolean newGroup = keys.putIfAbsent(key, true) == null;
|
||||
if (newGroup) {
|
||||
observer.onNext(buildObservableFor(source, key));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static <K, R> GroupedObservable<K, R> buildObservableFor(Observable<KeyValue<K, R>> source, final K key) {
|
||||
final Observable<R> observable = source.filter(new Func1<KeyValue<K, R>, Boolean>() {
|
||||
@Override
|
||||
public Boolean call(KeyValue<K, R> pair) {
|
||||
return key.equals(pair.key);
|
||||
}
|
||||
}).map(new Func1<KeyValue<K, R>, R>() {
|
||||
@Override
|
||||
public R call(KeyValue<K, R> pair) {
|
||||
return pair.value;
|
||||
}
|
||||
});
|
||||
return new GroupedObservable<K, R>(key, new Func1<Observer<R>, Subscription>() {
|
||||
|
||||
@Override
|
||||
public Subscription call(Observer<R> observer) {
|
||||
return observable.subscribe(observer);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private static class KeyValue<K, V> {
|
||||
private final K key;
|
||||
private final V value;
|
||||
|
||||
private KeyValue(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
137
HMCLAPI/src/main/java/rx/operators/OperatorTakeUntil.java
Normal file
137
HMCLAPI/src/main/java/rx/operators/OperatorTakeUntil.java
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* 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 rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public class OperatorTakeUntil {
|
||||
|
||||
/**
|
||||
* Returns the values from the source observable sequence until the other observable sequence produces a value.
|
||||
*
|
||||
* @param source
|
||||
* the source sequence to propagate elements for.
|
||||
* @param other
|
||||
* the observable sequence that terminates propagation of elements of the source sequence.
|
||||
* @param <T>
|
||||
* the type of source.
|
||||
* @param <E>
|
||||
* the other type.
|
||||
* @return An observable sequence containing the elements of the source sequence up to the point the other sequence interrupted further propagation.
|
||||
*/
|
||||
public static <T, E> Observable<T> takeUntil(final Observable<T> source, final Observable<E> other) {
|
||||
Observable<Notification<T>> s = Observable.create(new SourceObservable<T>(source));
|
||||
Observable<Notification<T>> o = Observable.create(new OtherObservable<T, E>(other));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
/**
|
||||
* In JDK 7 we could use 'varargs' instead of 'unchecked'.
|
||||
* See http://stackoverflow.com/questions/1445233/is-it-possible-to-solve-the-a-generic-array-of-t-is-created-for-a-varargs-param
|
||||
* and http://hg.openjdk.java.net/jdk7/tl/langtools/rev/46cf751559ae
|
||||
*/
|
||||
Observable<Notification<T>> result = Observable.merge(s, o);
|
||||
|
||||
return result.takeWhile(new Func1<Notification<T>, Boolean>() {
|
||||
@Override
|
||||
public Boolean call(Notification<T> notification) {
|
||||
return !notification.halt;
|
||||
}
|
||||
}).map(new Func1<Notification<T>, T>() {
|
||||
@Override
|
||||
public T call(Notification<T> notification) {
|
||||
return notification.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class Notification<T> {
|
||||
private final boolean halt;
|
||||
private final T value;
|
||||
|
||||
public static <T> Notification<T> value(T value) {
|
||||
return new Notification<T>(false, value);
|
||||
}
|
||||
|
||||
public static <T> Notification<T> halt() {
|
||||
return new Notification<T>(true, null);
|
||||
}
|
||||
|
||||
private Notification(boolean halt, T value) {
|
||||
this.halt = halt;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SourceObservable<T> implements Func1<Observer<Notification<T>>, Subscription> {
|
||||
private final Observable<T> sequence;
|
||||
|
||||
private SourceObservable(Observable<T> sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<Notification<T>> notificationObserver) {
|
||||
return sequence.subscribe(new Observer<T>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
notificationObserver.onNext(Notification.<T> halt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
notificationObserver.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
notificationObserver.onNext(Notification.value(args));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static class OtherObservable<T, E> implements Func1<Observer<Notification<T>>, Subscription> {
|
||||
private final Observable<E> sequence;
|
||||
|
||||
private OtherObservable(Observable<E> sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subscription call(final Observer<Notification<T>> notificationObserver) {
|
||||
return sequence.subscribe(new Observer<E>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
notificationObserver.onError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(E args) {
|
||||
notificationObserver.onNext(Notification.<T> halt());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
85
HMCLAPI/src/main/java/rx/operators/OperatorToIterator.java
Normal file
85
HMCLAPI/src/main/java/rx/operators/OperatorToIterator.java
Normal file
@@ -0,0 +1,85 @@
|
||||
package rx.operators;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import rx.Notification;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.util.Exceptions;
|
||||
|
||||
/**
|
||||
* @see https://github.com/Netflix/RxJava/issues/50
|
||||
*/
|
||||
public class OperatorToIterator {
|
||||
|
||||
/**
|
||||
* Returns an iterator that iterates all values of the observable.
|
||||
*
|
||||
* @param that
|
||||
* an observable sequence to get an iterator for.
|
||||
* @param <T>
|
||||
* the type of source.
|
||||
* @return the iterator that could be used to iterate over the elements of the observable.
|
||||
*/
|
||||
public static <T> Iterator<T> toIterator(Observable<T> that) {
|
||||
final BlockingQueue<Notification<T>> notifications = new LinkedBlockingQueue<Notification<T>>();
|
||||
|
||||
Observable.materialize(that).subscribe(new Observer<Notification<T>>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Notification<T> args) {
|
||||
notifications.offer(args);
|
||||
}
|
||||
});
|
||||
|
||||
return new Iterator<T>() {
|
||||
private Notification<T> buf;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (buf == null) {
|
||||
buf = take();
|
||||
}
|
||||
return !buf.isOnCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (buf == null) {
|
||||
buf = take();
|
||||
}
|
||||
if (buf.isOnError()) {
|
||||
throw Exceptions.propagate(buf.getException());
|
||||
}
|
||||
|
||||
T result = buf.getValue();
|
||||
buf = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
private Notification<T> take() {
|
||||
try {
|
||||
return notifications.take();
|
||||
} catch (InterruptedException e) {
|
||||
throw Exceptions.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Read-only iterator");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
60
HMCLAPI/src/main/java/rx/operators/ScheduledObserver.java
Normal file
60
HMCLAPI/src/main/java/rx/operators/ScheduledObserver.java
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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 rx.Observer;
|
||||
import rx.Scheduler;
|
||||
import rx.util.functions.Action0;
|
||||
|
||||
/* package */class ScheduledObserver<T> implements Observer<T> {
|
||||
private final Observer<T> underlying;
|
||||
private final Scheduler scheduler;
|
||||
|
||||
public ScheduledObserver(Observer<T> underlying, Scheduler scheduler) {
|
||||
this.underlying = underlying;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
scheduler.schedule(new Action0() {
|
||||
@Override
|
||||
public void call() {
|
||||
underlying.onCompleted();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Exception e) {
|
||||
scheduler.schedule(new Action0() {
|
||||
@Override
|
||||
public void call() {
|
||||
underlying.onError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(final T args) {
|
||||
scheduler.schedule(new Action0() {
|
||||
@Override
|
||||
public void call() {
|
||||
underlying.onNext(args);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
23
HMCLAPI/src/main/java/rx/operators/package.html
Normal file
23
HMCLAPI/src/main/java/rx/operators/package.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<body>
|
||||
<p>Operators that allow composing Observables to transform and
|
||||
manipulate data in an asynchronous, functional and thread-safe manner.</p>
|
||||
<p>The operators are all exposed via the ObservableExtensions class</p>
|
||||
|
||||
</body>
|
||||
45
HMCLAPI/src/main/java/rx/package-info.java
Normal file
45
HMCLAPI/src/main/java/rx/package-info.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* <p>Rx Observables</p>
|
||||
*
|
||||
* <p>A library that enables subscribing to and composing asynchronous events and
|
||||
* callbacks.</p>
|
||||
* <p>The Observable/Observer interfaces and associated operators (in
|
||||
* the .operations package) are inspired by and attempt to conform to the
|
||||
* Reactive Rx library in Microsoft .Net.</p>
|
||||
* <p>
|
||||
* More information can be found at <a
|
||||
* href="http://msdn.microsoft.com/en-us/data/gg577609">http://msdn.microsoft.com/en-us/data/gg577609</a>.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <p>Compared with the Microsoft implementation:
|
||||
* <ul>
|
||||
* <li>Observable == IObservable</li>
|
||||
* <li>Observer == IObserver</li>
|
||||
* <li>Subscription == IDisposable</li>
|
||||
* <li>ObservableExtensions == Observable</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>Services which intend on exposing data asynchronously and wish
|
||||
* to allow reactive processing and composition can implement the
|
||||
* Watchable interface which then allows Watchers to subscribe to them
|
||||
* and receive events.</p>
|
||||
* <p>Usage examples can be found on the Watchable and Watcher
|
||||
* classes.</p>
|
||||
*/
|
||||
package rx;
|
||||
41
HMCLAPI/src/main/java/rx/plugins/RxJavaErrorHandler.java
Normal file
41
HMCLAPI/src/main/java/rx/plugins/RxJavaErrorHandler.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.plugins;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
|
||||
/**
|
||||
* Abstract class for defining error handling logic in addition to the normal {@link Observer#onError(Exception)} behavior.
|
||||
* <p>
|
||||
* For example, all Exceptions can be logged using this handler even if {@link Observer#onError(Exception)} is ignored or not provided when an {@link Observable} is subscribed to.
|
||||
* <p>
|
||||
* See {@link RxJavaPlugins} or the RxJava GitHub Wiki for information on configuring plugins: <a
|
||||
* href="https://github.com/Netflix/RxJava/wiki/Plugins">https://github.com/Netflix/RxJava/wiki/Plugins</a>.
|
||||
*/
|
||||
public abstract class RxJavaErrorHandler {
|
||||
|
||||
/**
|
||||
* Receives all Exceptions from an {@link Observable} passed to {@link Observer#onError(Exception)}.
|
||||
*
|
||||
* @param e
|
||||
* Exception
|
||||
*/
|
||||
public void handleError(Exception e) {
|
||||
// do nothing by default
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.plugins;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link RxJavaErrorHandler} that does nothing.
|
||||
*
|
||||
* @ExcludeFromJavadoc
|
||||
*/
|
||||
public class RxJavaErrorHandlerDefault extends RxJavaErrorHandler {
|
||||
|
||||
private static RxJavaErrorHandlerDefault INSTANCE = new RxJavaErrorHandlerDefault();
|
||||
|
||||
public static RxJavaErrorHandler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* 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.plugins;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
/**
|
||||
* Abstract ExecutionHook with invocations at different lifecycle points of {@link Observable} execution with a default no-op implementation.
|
||||
* <p>
|
||||
* See {@link RxJavaPlugins} or the RxJava GitHub Wiki for information on configuring plugins: <a
|
||||
* href="https://github.com/Netflix/RxJava/wiki/Plugins">https://github.com/Netflix/RxJava/wiki/Plugins</a>.
|
||||
* <p>
|
||||
* <b>Note on thread-safety and performance</b>
|
||||
* <p>
|
||||
* A single implementation of this class will be used globally so methods on this class will be invoked concurrently from multiple threads so all functionality must be thread-safe.
|
||||
* <p>
|
||||
* Methods are also invoked synchronously and will add to execution time of the observable so all behavior should be fast. If anything time-consuming is to be done it should be spawned asynchronously
|
||||
* onto separate worker threads.
|
||||
*
|
||||
* */
|
||||
public abstract class RxJavaObservableExecutionHook {
|
||||
|
||||
/**
|
||||
* Invoked before {@link Observable#subscribe(rx.Observer)} is about to be executed.
|
||||
* <p>
|
||||
* This can be used to decorate or replace the <code>onSubscribe</code> function or just perform extra logging, metrics and other such things and pass-thru the function.
|
||||
*
|
||||
* @param observableInstance
|
||||
* The executing {@link Observable} instance.
|
||||
* @param onSubscribe
|
||||
* original {@link Func1}<{@link Observer}{@code<T>}, {@link Subscription}> to be executed
|
||||
* @return {@link Func1}<{@link Observer}{@code<T>}, {@link Subscription}> function that can be modified, decorated, replaced or just returned as a pass-thru.
|
||||
*/
|
||||
public <T> Func1<Observer<T>, Subscription> onSubscribeStart(Observable<T> observableInstance, Func1<Observer<T>, Subscription> onSubscribe) {
|
||||
// pass-thru by default
|
||||
return onSubscribe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked after successful execution of {@link Observable#subscribe(rx.Observer)} with returned {@link Subscription}.
|
||||
* <p>
|
||||
* This can be used to decorate or replace the {@link Subscription} instance or just perform extra logging, metrics and other such things and pass-thru the subscription.
|
||||
*
|
||||
* @param observableInstance
|
||||
* The executing {@link Observable} instance.
|
||||
* @param subscription
|
||||
* original {@link Subscription}
|
||||
* @return {@link Subscription} subscription that can be modified, decorated, replaced or just returned as a pass-thru.
|
||||
*/
|
||||
public <T> Subscription onSubscribeReturn(Observable<T> observableInstance, Subscription subscription) {
|
||||
// pass-thru by default
|
||||
return subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked after failed execution of {@link Observable#subscribe(Observer)} with thrown Exception.
|
||||
* <p>
|
||||
* This is NOT errors emitted via {@link Observer#onError(Exception)} but exceptions thrown when attempting
|
||||
* to subscribe to a {@link Func1}<{@link Observer}{@code<T>}, {@link Subscription}>.
|
||||
*
|
||||
* @param observableInstance
|
||||
* The executing {@link Observable} instance.
|
||||
* @param e
|
||||
* Exception thrown by {@link Observable#subscribe(Observer)}
|
||||
* @return Exception that can be decorated, replaced or just returned as a pass-thru.
|
||||
*/
|
||||
public <T> Exception onSubscribeError(Observable<T> observableInstance, Exception e) {
|
||||
// pass-thru by default
|
||||
return e;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.plugins;
|
||||
|
||||
/**
|
||||
* Default no-op implementation of {@link RxJavaObservableExecutionHook}
|
||||
*/
|
||||
/* package */class RxJavaObservableExecutionHookDefault extends RxJavaObservableExecutionHook {
|
||||
|
||||
private static RxJavaObservableExecutionHookDefault INSTANCE = new RxJavaObservableExecutionHookDefault();
|
||||
|
||||
public static RxJavaObservableExecutionHook getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
147
HMCLAPI/src/main/java/rx/plugins/RxJavaPlugins.java
Normal file
147
HMCLAPI/src/main/java/rx/plugins/RxJavaPlugins.java
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* 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.plugins;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Registry for plugin implementations that allows global override and handles the retrieval of correct implementation based on order of precedence:
|
||||
* <ol>
|
||||
* <li>plugin registered globally via <code>register</code> methods in this class</li>
|
||||
* <li>plugin registered and retrieved using {@link java.lang.System#getProperty(String)} (see get methods for property names)</li>
|
||||
* <li>default implementation</li>
|
||||
* </ol>
|
||||
* See the RxJava GitHub Wiki for more information: <a href="https://github.com/Netflix/RxJava/wiki/Plugins">https://github.com/Netflix/RxJava/wiki/Plugins</a>.
|
||||
*/
|
||||
public class RxJavaPlugins {
|
||||
private final static RxJavaPlugins INSTANCE = new RxJavaPlugins();
|
||||
|
||||
private final AtomicReference<RxJavaErrorHandler> errorHandler = new AtomicReference<RxJavaErrorHandler>();
|
||||
private final AtomicReference<RxJavaObservableExecutionHook> observableExecutionHook = new AtomicReference<RxJavaObservableExecutionHook>();
|
||||
|
||||
private RxJavaPlugins() {
|
||||
|
||||
}
|
||||
|
||||
public static RxJavaPlugins getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve instance of {@link RxJavaErrorHandler} to use based on order of precedence as defined in {@link RxJavaPlugins} class header.
|
||||
* <p>
|
||||
* Override default by using {@link #registerErrorHandler(RxJavaErrorHandler)} or setting property: <code>rxjava.plugin.RxJavaErrorHandler.implementation</code> with the full classname to
|
||||
* load.
|
||||
*
|
||||
* @return {@link RxJavaErrorHandler} implementation to use
|
||||
*/
|
||||
public RxJavaErrorHandler getErrorHandler() {
|
||||
if (errorHandler.get() == null) {
|
||||
// check for an implementation from System.getProperty first
|
||||
Object impl = getPluginImplementationViaProperty(RxJavaErrorHandler.class);
|
||||
if (impl == null) {
|
||||
// nothing set via properties so initialize with default
|
||||
errorHandler.compareAndSet(null, RxJavaErrorHandlerDefault.getInstance());
|
||||
// we don't return from here but call get() again in case of thread-race so the winner will always get returned
|
||||
} else {
|
||||
// we received an implementation from the system property so use it
|
||||
errorHandler.compareAndSet(null, (RxJavaErrorHandler) impl);
|
||||
}
|
||||
}
|
||||
return errorHandler.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link RxJavaErrorHandler} implementation as a global override of any injected or default implementations.
|
||||
*
|
||||
* @param impl
|
||||
* {@link RxJavaErrorHandler} implementation
|
||||
* @throws IllegalStateException
|
||||
* if called more than once or after the default was initialized (if usage occurs before trying to register)
|
||||
*/
|
||||
public void registerErrorHandler(RxJavaErrorHandler impl) {
|
||||
if (!errorHandler.compareAndSet(null, impl)) {
|
||||
throw new IllegalStateException("Another strategy was already registered.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve instance of {@link RxJavaObservableExecutionHook} to use based on order of precedence as defined in {@link RxJavaPlugins} class header.
|
||||
* <p>
|
||||
* Override default by using {@link #registerObservableExecutionHook(RxJavaObservableExecutionHook)} or setting property: <code>rxjava.plugin.RxJavaObservableExecutionHook.implementation</code>
|
||||
* with the full classname to load.
|
||||
*
|
||||
* @return {@link RxJavaObservableExecutionHook} implementation to use
|
||||
*/
|
||||
public RxJavaObservableExecutionHook getObservableExecutionHook() {
|
||||
if (observableExecutionHook.get() == null) {
|
||||
// check for an implementation from System.getProperty first
|
||||
Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class);
|
||||
if (impl == null) {
|
||||
// nothing set via properties so initialize with default
|
||||
observableExecutionHook.compareAndSet(null, RxJavaObservableExecutionHookDefault.getInstance());
|
||||
// we don't return from here but call get() again in case of thread-race so the winner will always get returned
|
||||
} else {
|
||||
// we received an implementation from the system property so use it
|
||||
observableExecutionHook.compareAndSet(null, (RxJavaObservableExecutionHook) impl);
|
||||
}
|
||||
}
|
||||
return observableExecutionHook.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link RxJavaObservableExecutionHook} implementation as a global override of any injected or default implementations.
|
||||
*
|
||||
* @param impl
|
||||
* {@link RxJavaObservableExecutionHook} implementation
|
||||
* @throws IllegalStateException
|
||||
* if called more than once or after the default was initialized (if usage occurs before trying to register)
|
||||
*/
|
||||
public void registerObservableExecutionHook(RxJavaObservableExecutionHook impl) {
|
||||
if (!observableExecutionHook.compareAndSet(null, impl)) {
|
||||
throw new IllegalStateException("Another strategy was already registered.");
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getPluginImplementationViaProperty(Class<?> pluginClass) {
|
||||
String classSimpleName = pluginClass.getSimpleName();
|
||||
/*
|
||||
* Check system properties for plugin class.
|
||||
* <p>
|
||||
* This will only happen during system startup thus it's okay to use the synchronized System.getProperties
|
||||
* as it will never get called in normal operations.
|
||||
*/
|
||||
String implementingClass = System.getProperty("rxjava.plugin." + classSimpleName + ".implementation");
|
||||
if (implementingClass != null) {
|
||||
try {
|
||||
Class<?> cls = Class.forName(implementingClass);
|
||||
// narrow the scope (cast) to the type we're expecting
|
||||
cls = cls.asSubclass(pluginClass);
|
||||
return cls.newInstance();
|
||||
} catch (ClassCastException e) {
|
||||
throw new RuntimeException(classSimpleName + " implementation is not an instance of " + classSimpleName + ": " + implementingClass);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(classSimpleName + " implementation class not found: " + implementingClass, e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(classSimpleName + " implementation not able to be instantiated: " + implementingClass, e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(classSimpleName + " implementation not able to be accessed: " + implementingClass, e);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
HMCLAPI/src/main/java/rx/subjects/Subject.java
Normal file
65
HMCLAPI/src/main/java/rx/subjects/Subject.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package rx.subjects;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import rx.Observable;
|
||||
import rx.Observer;
|
||||
import rx.Subscription;
|
||||
import rx.util.AtomicObservableSubscription;
|
||||
import rx.util.SynchronizedObserver;
|
||||
import rx.util.functions.Func1;
|
||||
|
||||
public class Subject<T> extends Observable<T> implements Observer<T> {
|
||||
public static <T> Subject<T> create() {
|
||||
final ConcurrentHashMap<Subscription, Observer<T>> observers = new ConcurrentHashMap<Subscription, Observer<T>>();
|
||||
|
||||
Func1<Observer<T>, Subscription> onSubscribe = new Func1<Observer<T>, Subscription>() {
|
||||
@Override
|
||||
public Subscription call(Observer<T> observer) {
|
||||
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
|
||||
|
||||
subscription.wrap(new Subscription() {
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
// on unsubscribe remove it from the map of outbound observers to notify
|
||||
observers.remove(subscription);
|
||||
}
|
||||
});
|
||||
|
||||
// on subscribe add it to the map of outbound observers to notify
|
||||
observers.put(subscription, new SynchronizedObserver<T>(observer, subscription));
|
||||
return subscription;
|
||||
}
|
||||
};
|
||||
|
||||
return new Subject<T>(onSubscribe, observers);
|
||||
}
|
||||
|
||||
private final ConcurrentHashMap<Subscription, Observer<T>> observers;
|
||||
|
||||
protected Subject(Func1<Observer<T>, Subscription> onSubscribe, ConcurrentHashMap<Subscription, Observer<T>> observers) {
|
||||
super(onSubscribe);
|
||||
this.observers = observers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
for (Observer<T> observer : observers.values()) {
|
||||
observer.onCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
for (Observer<T> observer : observers.values()) {
|
||||
observer.onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
for (Observer<T> observer : observers.values()) {
|
||||
observer.onNext(args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package rx.subscriptions;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.Subscription;
|
||||
|
||||
/**
|
||||
* Subscription that can be checked for status such as in a loop inside an {@link Observable} to exit the loop if unsubscribed.
|
||||
*
|
||||
* @see Rx.Net equivalent BooleanDisposable at http://msdn.microsoft.com/en-us/library/system.reactive.disposables.booleandisposable(v=vs.103).aspx
|
||||
*/
|
||||
public class BooleanSubscription implements Subscription {
|
||||
|
||||
private final AtomicBoolean unsubscribed = new AtomicBoolean(false);
|
||||
|
||||
public boolean isUnsubscribed() {
|
||||
return unsubscribed.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
unsubscribed.set(true);
|
||||
}
|
||||
|
||||
}
|
||||
58
HMCLAPI/src/main/java/rx/subscriptions/Subscriptions.java
Normal file
58
HMCLAPI/src/main/java/rx/subscriptions/Subscriptions.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package rx.subscriptions;
|
||||
|
||||
import rx.Subscription;
|
||||
import rx.util.functions.Action0;
|
||||
import rx.util.functions.FuncN;
|
||||
import rx.util.functions.Functions;
|
||||
|
||||
public class Subscriptions {
|
||||
/**
|
||||
* A {@link Subscription} that does nothing.
|
||||
*
|
||||
* @return {@link Subscription}
|
||||
*/
|
||||
public static Subscription empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Subscription} implemented via a Func
|
||||
*
|
||||
* @return {@link Subscription}
|
||||
*/
|
||||
public static Subscription create(final Action0 unsubscribe) {
|
||||
return new Subscription() {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
unsubscribe.call();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Subscription} implemented via an anonymous function (such as closures from other languages).
|
||||
*
|
||||
* @return {@link Subscription}
|
||||
*/
|
||||
public static Subscription create(final Object unsubscribe) {
|
||||
final FuncN<?> f = Functions.from(unsubscribe);
|
||||
return new Subscription() {
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
f.call();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Subscription} that does nothing when its unsubscribe method is called.
|
||||
*/
|
||||
private static Subscription EMPTY = new Subscription() {
|
||||
public void unsubscribe() {
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.util;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import rx.Subscription;
|
||||
|
||||
/**
|
||||
* Thread-safe wrapper around Observable Subscription that ensures unsubscribe can be called only once.
|
||||
* <p>
|
||||
* Also used to:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>allow the AtomicObserver to have access to the subscription in asynchronous execution for checking if unsubscribed occurred without onComplete/onError.</li>
|
||||
* <li>handle both synchronous and asynchronous subscribe() execution flows</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class AtomicObservableSubscription implements Subscription {
|
||||
|
||||
private AtomicReference<Subscription> actualSubscription = new AtomicReference<Subscription>();
|
||||
private AtomicBoolean unsubscribed = new AtomicBoolean(false);
|
||||
|
||||
public AtomicObservableSubscription() {
|
||||
|
||||
}
|
||||
|
||||
public AtomicObservableSubscription(Subscription actualSubscription) {
|
||||
this.actualSubscription.set(actualSubscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the actual subscription once it exists (if it wasn't available when constructed)
|
||||
*
|
||||
* @param actualSubscription
|
||||
* @throws IllegalStateException
|
||||
* if trying to set more than once (or use this method after setting via constructor)
|
||||
*/
|
||||
public AtomicObservableSubscription wrap(Subscription actualSubscription) {
|
||||
if (!this.actualSubscription.compareAndSet(null, actualSubscription)) {
|
||||
throw new IllegalStateException("Can not set subscription more than once.");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
// get the real thing and set to null in an atomic operation so we will only ever call unsubscribe once
|
||||
Subscription actual = actualSubscription.getAndSet(null);
|
||||
// if it's not null we will unsubscribe
|
||||
if (actual != null) {
|
||||
actual.unsubscribe();
|
||||
unsubscribed.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isUnsubscribed() {
|
||||
return unsubscribed.get();
|
||||
}
|
||||
}
|
||||
97
HMCLAPI/src/main/java/rx/util/AtomicObserver.java
Normal file
97
HMCLAPI/src/main/java/rx/util/AtomicObserver.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package rx.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import rx.Observer;
|
||||
import rx.plugins.RxJavaPlugins;
|
||||
|
||||
/**
|
||||
* Wrapper around Observer to ensure compliance with Rx contract.
|
||||
* <p>
|
||||
* The following is taken from the Rx Design Guidelines document: http://go.microsoft.com/fwlink/?LinkID=205219
|
||||
* <pre>
|
||||
* Messages sent to instances of the IObserver interface follow the following grammar:
|
||||
*
|
||||
* OnNext* (OnCompleted | OnError)?
|
||||
*
|
||||
* This grammar allows observable sequences to send any amount (0 or more) of OnNext messages to the subscribed
|
||||
* observer instance, optionally followed by a single success (OnCompleted) or failure (OnError) message.
|
||||
*
|
||||
* The single message indicating that an observable sequence has finished ensures that consumers of the observable
|
||||
* sequence can deterministically establish that it is safe to perform cleanup operations.
|
||||
*
|
||||
* A single failure further ensures that abort semantics can be maintained for operators that work on
|
||||
* multiple observable sequences (see paragraph 6.6).
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This wrapper will do the following:
|
||||
* <ul>
|
||||
* <li>Allow only single execution of either onError or onCompleted.</li>
|
||||
* <li>Once an onComplete or onError are performed, no further calls can be executed</li>
|
||||
* <li>If unsubscribe is called, this means we call completed() and don't allow any further onNext calls.</li>
|
||||
* <li>When onError or onComplete occur it will unsubscribe from the Observable (if executing asynchronously).</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* It will not synchronize onNext execution. Use the {@link SynchronizedObserver} to do that.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class AtomicObserver<T> implements Observer<T> {
|
||||
|
||||
private final Observer<T> actual;
|
||||
private final AtomicBoolean isFinished = new AtomicBoolean(false);
|
||||
private final AtomicObservableSubscription subscription;
|
||||
|
||||
public AtomicObserver(AtomicObservableSubscription subscription, Observer<T> actual) {
|
||||
this.subscription = subscription;
|
||||
this.actual = actual;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (isFinished.compareAndSet(false, true)) {
|
||||
try {
|
||||
actual.onCompleted();
|
||||
} catch (Exception e) {
|
||||
// handle errors if the onCompleted implementation fails, not just if the Observable fails
|
||||
onError(e);
|
||||
}
|
||||
// auto-unsubscribe
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
if (isFinished.compareAndSet(false, true)) {
|
||||
try {
|
||||
actual.onError(e);
|
||||
} catch (Exception e2) {
|
||||
// if the onError itself fails then pass to the plugin
|
||||
// see https://github.com/Netflix/RxJava/issues/216 for further discussion
|
||||
RxJavaPlugins.getInstance().getErrorHandler().handleError(e);
|
||||
RxJavaPlugins.getInstance().getErrorHandler().handleError(e2);
|
||||
// and throw exception despite that not being proper for Rx
|
||||
// https://github.com/Netflix/RxJava/issues/198
|
||||
throw new RuntimeException("Error occurred when trying to propagate error to Observer.onError", new CompositeException(Arrays.asList(e, e2)));
|
||||
}
|
||||
// auto-unsubscribe
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T args) {
|
||||
try {
|
||||
if (!isFinished.get()) {
|
||||
actual.onNext(args);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// handle errors if the onNext implementation fails, not just if the Observable fails
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
65
HMCLAPI/src/main/java/rx/util/CompositeException.java
Normal file
65
HMCLAPI/src/main/java/rx/util/CompositeException.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exception that is a composite of 1 or more other exceptions.
|
||||
* <p>
|
||||
* The <code>getMessage()</code> will return a concatenation of the composite exceptions.
|
||||
*/
|
||||
public class CompositeException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 3026362227162912146L;
|
||||
|
||||
private final List<Exception> exceptions;
|
||||
private final String message;
|
||||
|
||||
public CompositeException(String messagePrefix, Collection<Exception> errors) {
|
||||
StringBuilder _message = new StringBuilder();
|
||||
if (messagePrefix != null) {
|
||||
_message.append(messagePrefix).append(" => ");
|
||||
}
|
||||
|
||||
List<Exception> _exceptions = new ArrayList<Exception>();
|
||||
for (Exception e : errors) {
|
||||
_exceptions.add(e);
|
||||
if (_message.length() > 0) {
|
||||
_message.append(", ");
|
||||
}
|
||||
_message.append(e.getClass().getSimpleName()).append(":").append(e.getMessage());
|
||||
}
|
||||
this.exceptions = Collections.unmodifiableList(_exceptions);
|
||||
this.message = _message.toString();
|
||||
}
|
||||
|
||||
public CompositeException(Collection<Exception> errors) {
|
||||
this(null, errors);
|
||||
}
|
||||
|
||||
public List<Exception> getExceptions() {
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
31
HMCLAPI/src/main/java/rx/util/Exceptions.java
Normal file
31
HMCLAPI/src/main/java/rx/util/Exceptions.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.util;
|
||||
|
||||
public class Exceptions {
|
||||
private Exceptions() {
|
||||
|
||||
}
|
||||
|
||||
public static RuntimeException propagate(Throwable t) {
|
||||
if (t instanceof RuntimeException) {
|
||||
throw (RuntimeException) t;
|
||||
} else {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
75
HMCLAPI/src/main/java/rx/util/Range.java
Normal file
75
HMCLAPI/src/main/java/rx/util/Range.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 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.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public final class Range implements Iterable<Integer> {
|
||||
private final int start;
|
||||
private final int end;
|
||||
private final int step;
|
||||
|
||||
public static Range createWithCount(int start, int count) {
|
||||
return create(start, start + count);
|
||||
}
|
||||
|
||||
public static Range create(int start, int end) {
|
||||
return new Range(start, end, 1);
|
||||
}
|
||||
|
||||
public static Range createWithStep(int start, int end, int step) {
|
||||
return new Range(start, end, step);
|
||||
}
|
||||
|
||||
private Range(int start, int end, int step) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Integer> iterator() {
|
||||
return new Iterator<Integer>() {
|
||||
private int current = start;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return current < end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException("No more elements");
|
||||
}
|
||||
int result = current;
|
||||
current += step;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Read only iterator");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Range (" + start + ", " + end + "), step " + step;
|
||||
}
|
||||
}
|
||||
108
HMCLAPI/src/main/java/rx/util/SynchronizedObserver.java
Normal file
108
HMCLAPI/src/main/java/rx/util/SynchronizedObserver.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 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.util;
|
||||
|
||||
import rx.Observer;
|
||||
|
||||
/**
|
||||
* A thread-safe Observer for transitioning states in operators.
|
||||
* <p>
|
||||
* Execution rules are:
|
||||
* <ul>
|
||||
* <li>Allow only single-threaded, synchronous, ordered execution of onNext, onCompleted, onError</li>
|
||||
* <li>Once an onComplete or onError are performed, no further calls can be executed</li>
|
||||
* <li>If unsubscribe is called, this means we call completed() and don't allow any further onNext calls.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public final class SynchronizedObserver<T> implements Observer<T> {
|
||||
|
||||
/**
|
||||
* Intrinsic synchronized locking with double-check short-circuiting was chosen after testing several other implementations.
|
||||
*
|
||||
* The code and results can be found here:
|
||||
* - https://github.com/benjchristensen/JavaLockPerformanceTests/tree/master/results/Observer
|
||||
* - https://github.com/benjchristensen/JavaLockPerformanceTests/tree/master/src/com/benjchristensen/performance/locks/Observer
|
||||
*
|
||||
* The major characteristic that made me choose synchronized instead of Reentrant or a customer AbstractQueueSynchronizer implementation
|
||||
* is that intrinsic locking performed better when nested, and AtomicObserver will end up nested most of the time since Rx is
|
||||
* compositional by its very nature.
|
||||
*
|
||||
* // TODO composing of this class should rarely happen now with updated design so this decision should be revisited
|
||||
*/
|
||||
|
||||
private final Observer<T> observer;
|
||||
private final AtomicObservableSubscription subscription;
|
||||
private volatile boolean finishRequested = false;
|
||||
private volatile boolean finished = false;
|
||||
|
||||
public SynchronizedObserver(Observer<T> Observer, AtomicObservableSubscription subscription) {
|
||||
this.observer = Observer;
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T arg) {
|
||||
if (finished || finishRequested || subscription.isUnsubscribed()) {
|
||||
// if we're already stopped, or a finish request has been received, we won't allow further onNext requests
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
// check again since this could have changed while waiting
|
||||
if (finished || finishRequested || subscription.isUnsubscribed()) {
|
||||
// if we're already stopped, or a finish request has been received, we won't allow further onNext requests
|
||||
return;
|
||||
}
|
||||
observer.onNext(arg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
if (finished || subscription.isUnsubscribed()) {
|
||||
// another thread has already finished us, so we won't proceed
|
||||
return;
|
||||
}
|
||||
finishRequested = true;
|
||||
synchronized (this) {
|
||||
// check again since this could have changed while waiting
|
||||
if (finished || subscription.isUnsubscribed()) {
|
||||
return;
|
||||
}
|
||||
observer.onError(e);
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (finished || subscription.isUnsubscribed()) {
|
||||
// another thread has already finished us, so we won't proceed
|
||||
return;
|
||||
}
|
||||
finishRequested = true;
|
||||
synchronized (this) {
|
||||
// check again since this could have changed while waiting
|
||||
if (finished || subscription.isUnsubscribed()) {
|
||||
return;
|
||||
}
|
||||
observer.onCompleted();
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Action0.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Action0.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Action0 extends Function {
|
||||
public void call();
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Action1.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Action1.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Action1<T1> extends Function {
|
||||
public void call(T1 t1);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Action2.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Action2.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Action2<T1, T2> extends Function {
|
||||
public void call(T1 t1, T2 t2);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Action3.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Action3.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Action3<T1, T2, T3> extends Function {
|
||||
public void call(T1 t1, T2 t2, T3 t3);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Func0.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Func0.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Func0<R> extends Function {
|
||||
public R call();
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Func1.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Func1.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Func1<T1, R> extends Function {
|
||||
public R call(T1 t1);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Func2.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Func2.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Func2<T1, T2, R> extends Function {
|
||||
public R call(T1 t1, T2 t2);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Func3.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Func3.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Func3<T1, T2, T3, R> extends Function {
|
||||
public R call(T1 t1, T2 t2, T3 t3);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Func4.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Func4.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Func4<T1, T2, T3, T4, R> extends Function {
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Func5.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Func5.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Func5<T1, T2, T3, T4, T5, R> extends Function {
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Func6.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Func6.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Func6<T1, T2, T3, T4, T5, T6, R> extends Function {
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Func7.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Func7.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Func7<T1, T2, T3, T4, T5, T6, T7, R> extends Function {
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Func8.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Func8.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Func8<T1, T2, T3, T4, T5, T6, T7, T8, R> extends Function {
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/Func9.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/Func9.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface Func9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R> extends Function {
|
||||
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9);
|
||||
}
|
||||
20
HMCLAPI/src/main/java/rx/util/functions/FuncN.java
Normal file
20
HMCLAPI/src/main/java/rx/util/functions/FuncN.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface FuncN<R> extends Function {
|
||||
public R call(Object... args);
|
||||
}
|
||||
10
HMCLAPI/src/main/java/rx/util/functions/Function.java
Normal file
10
HMCLAPI/src/main/java/rx/util/functions/Function.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package rx.util.functions;
|
||||
|
||||
/**
|
||||
* All Func and Action interfaces extend from this.
|
||||
* <p>
|
||||
* Marker interface to allow isntanceof checks.
|
||||
*/
|
||||
public interface Function {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
public interface FunctionLanguageAdaptor {
|
||||
|
||||
/**
|
||||
* Invoke the function and return the results.
|
||||
*
|
||||
* @param function
|
||||
* @param args
|
||||
* @return Object results from function execution
|
||||
*/
|
||||
Object call(Object function, Object[] args);
|
||||
|
||||
/**
|
||||
* The Class of the Function that this adaptor serves.
|
||||
* <p>
|
||||
* Example: groovy.lang.Closure
|
||||
* <p>
|
||||
* This should not return classes of java.* packages.
|
||||
*
|
||||
* @return Class[] of classes that this adaptor should be invoked for.
|
||||
*/
|
||||
public Class<?>[] getFunctionClass();
|
||||
}
|
||||
579
HMCLAPI/src/main/java/rx/util/functions/Functions.java
Normal file
579
HMCLAPI/src/main/java/rx/util/functions/Functions.java
Normal file
@@ -0,0 +1,579 @@
|
||||
/**
|
||||
* 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.util.functions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.jackhuang.hellominecraft.logging.logger.Logger;
|
||||
|
||||
/**
|
||||
* Allows execution of functions from multiple different languages.
|
||||
* <p>
|
||||
* Language support is provided via implementations of {@link FunctionLanguageAdaptor}.
|
||||
* <p>
|
||||
* This class will dynamically look for known language adaptors on the classpath at startup or new ones can be registered using {@link #registerLanguageAdaptor(Class[], FunctionLanguageAdaptor)}.
|
||||
*/
|
||||
public class Functions {
|
||||
|
||||
private static final Logger logger = new Logger("Functions");
|
||||
|
||||
private final static ConcurrentHashMap<Class<?>, FunctionLanguageAdaptor> languageAdaptors = new ConcurrentHashMap<Class<?>, FunctionLanguageAdaptor>();
|
||||
|
||||
static {
|
||||
/* optimistically look for supported languages if they are in the classpath */
|
||||
loadLanguageAdaptor("Groovy");
|
||||
loadLanguageAdaptor("JRuby");
|
||||
loadLanguageAdaptor("Clojure");
|
||||
loadLanguageAdaptor("Scala");
|
||||
// as new languages arise we can add them here but this does not prevent someone from using 'registerLanguageAdaptor' directly
|
||||
}
|
||||
|
||||
private static boolean loadLanguageAdaptor(String name) {
|
||||
String className = "rx.lang." + name.toLowerCase() + "." + name + "Adaptor";
|
||||
try {
|
||||
Class<?> c = Class.forName(className);
|
||||
FunctionLanguageAdaptor a = (FunctionLanguageAdaptor) c.newInstance();
|
||||
registerLanguageAdaptor(a.getFunctionClass(), a);
|
||||
logger.info("Successfully loaded function language adaptor: " + name + " with path: " + className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.info("Could not find function language adaptor: " + name + " with path: " + className);
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed trying to initialize function language adaptor: " + className, e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void registerLanguageAdaptor(Class<?>[] functionClasses, FunctionLanguageAdaptor adaptor) {
|
||||
for (Class<?> functionClass : functionClasses) {
|
||||
if (functionClass.getPackage().getName().startsWith("java.")) {
|
||||
throw new IllegalArgumentException("FunctionLanguageAdaptor implementations can not specify java.lang.* classes.");
|
||||
}
|
||||
languageAdaptors.put(functionClass, adaptor);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeLanguageAdaptor(Class<?> functionClass) {
|
||||
languageAdaptors.remove(functionClass);
|
||||
}
|
||||
|
||||
public static Collection<FunctionLanguageAdaptor> getRegisteredLanguageAdaptors() {
|
||||
return languageAdaptors.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for determining the type of closure/function and executing it.
|
||||
*
|
||||
* @param function
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes" })
|
||||
public static FuncN from(final Object function) {
|
||||
if (function == null) {
|
||||
throw new RuntimeException("function is null. Can't send arguments to null function.");
|
||||
}
|
||||
|
||||
/* check for typed Rx Function implementation first */
|
||||
if (function instanceof Function) {
|
||||
return fromFunction((Function) function);
|
||||
} else {
|
||||
/* not an Rx Function so try language adaptors */
|
||||
|
||||
// check for language adaptor
|
||||
for (final Class c : languageAdaptors.keySet()) {
|
||||
if (c.isInstance(function)) {
|
||||
final FunctionLanguageAdaptor la = languageAdaptors.get(c);
|
||||
// found the language adaptor so wrap in FuncN and return
|
||||
return new FuncN() {
|
||||
|
||||
@Override
|
||||
public Object call(Object... args) {
|
||||
return la.call(function, args);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
// no language adaptor found
|
||||
}
|
||||
|
||||
// no support found
|
||||
throw new RuntimeException("Unsupported closure type: " + function.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// private static <R> R executionRxFunction(Function function, Object... args) {
|
||||
// // check Func* classes
|
||||
// if (function instanceof Func0) {
|
||||
// Func0<R> f = (Func0<R>) function;
|
||||
// if (args.length != 0) {
|
||||
// throw new RuntimeException("The closure was Func0 and expected no arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return (R) f.call();
|
||||
// } else if (function instanceof Func1) {
|
||||
// Func1<Object, R> f = (Func1<Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func1 and expected 1 argument, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0]);
|
||||
// } else if (function instanceof Func2) {
|
||||
// Func2<Object, Object, R> f = (Func2<Object, Object, R>) function;
|
||||
// if (args.length != 2) {
|
||||
// throw new RuntimeException("The closure was Func2 and expected 2 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1]);
|
||||
// } else if (function instanceof Func3) {
|
||||
// Func3<Object, Object, Object, R> f = (Func3<Object, Object, Object, R>) function;
|
||||
// if (args.length != 3) {
|
||||
// throw new RuntimeException("The closure was Func3 and expected 3 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return (R) f.call(args[0], args[1], args[2]);
|
||||
// } else if (function instanceof Func4) {
|
||||
// Func4<Object, Object, Object, Object, R> f = (Func4<Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func4 and expected 4 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3]);
|
||||
// } else if (function instanceof Func5) {
|
||||
// Func5<Object, Object, Object, Object, Object, R> f = (Func5<Object, Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func5 and expected 5 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3], args[4]);
|
||||
// } else if (function instanceof Func6) {
|
||||
// Func6<Object, Object, Object, Object, Object, Object, R> f = (Func6<Object, Object, Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func6 and expected 6 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5]);
|
||||
// } else if (function instanceof Func7) {
|
||||
// Func7<Object, Object, Object, Object, Object, Object, Object, R> f = (Func7<Object, Object, Object, Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func7 and expected 7 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
|
||||
// } else if (function instanceof Func8) {
|
||||
// Func8<Object, Object, Object, Object, Object, Object, Object, Object, R> f = (Func8<Object, Object, Object, Object, Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func8 and expected 8 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
|
||||
// } else if (function instanceof Func9) {
|
||||
// Func9<Object, Object, Object, Object, Object, Object, Object, Object, Object, R> f = (Func9<Object, Object, Object, Object, Object, Object, Object, Object, Object, R>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Func9 and expected 9 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
|
||||
// } else if (function instanceof FuncN) {
|
||||
// FuncN<R> f = (FuncN<R>) function;
|
||||
// return f.call(args);
|
||||
// } else if (function instanceof Action0) {
|
||||
// Action0 f = (Action0) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Action0 and expected 0 arguments, but we received: " + args.length);
|
||||
// }
|
||||
// f.call();
|
||||
// return null;
|
||||
// } else if (function instanceof Action1) {
|
||||
// Action1<Object> f = (Action1<Object>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Action1 and expected 1 argument, but we received: " + args.length);
|
||||
// }
|
||||
// f.call(args[0]);
|
||||
// return null;
|
||||
// } else if (function instanceof Action2) {
|
||||
// Action2<Object, Object> f = (Action2<Object, Object>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Action2 and expected 2 argument, but we received: " + args.length);
|
||||
// }
|
||||
// f.call(args[0], args[1]);
|
||||
// return null;
|
||||
// } else if (function instanceof Action3) {
|
||||
// Action3<Object, Object, Object> f = (Action3<Object, Object, Object>) function;
|
||||
// if (args.length != 1) {
|
||||
// throw new RuntimeException("The closure was Action1 and expected 1 argument, but we received: " + args.length);
|
||||
// }
|
||||
// f.call(args[0], args[1], args[2]);
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// throw new RuntimeException("Unknown implementation of Function: " + function.getClass().getSimpleName());
|
||||
// }
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private static FuncN fromFunction(Function function) {
|
||||
// check Func* classes
|
||||
if (function instanceof Func0) {
|
||||
return fromFunc((Func0) function);
|
||||
} else if (function instanceof Func1) {
|
||||
return fromFunc((Func1) function);
|
||||
} else if (function instanceof Func2) {
|
||||
return fromFunc((Func2) function);
|
||||
} else if (function instanceof Func3) {
|
||||
return fromFunc((Func3) function);
|
||||
} else if (function instanceof Func4) {
|
||||
return fromFunc((Func4) function);
|
||||
} else if (function instanceof Func5) {
|
||||
return fromFunc((Func5) function);
|
||||
} else if (function instanceof Func6) {
|
||||
return fromFunc((Func6) function);
|
||||
} else if (function instanceof Func7) {
|
||||
return fromFunc((Func7) function);
|
||||
} else if (function instanceof Func8) {
|
||||
return fromFunc((Func8) function);
|
||||
} else if (function instanceof Func9) {
|
||||
return fromFunc((Func9) function);
|
||||
} else if (function instanceof FuncN) {
|
||||
return (FuncN) function;
|
||||
} else if (function instanceof Action0) {
|
||||
return fromAction((Action0) function);
|
||||
} else if (function instanceof Action1) {
|
||||
return fromAction((Action1) function);
|
||||
} else if (function instanceof Action2) {
|
||||
return fromAction((Action2) function);
|
||||
} else if (function instanceof Action3) {
|
||||
return fromAction((Action3) function);
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unknown implementation of Function: " + function.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <R> FuncN<R> fromFunc(final Func0<R> f) {
|
||||
return new FuncN<R>() {
|
||||
|
||||
@Override
|
||||
public R call(Object... args) {
|
||||
if (args.length != 0) {
|
||||
throw new RuntimeException("Func0 expecting 0 arguments.");
|
||||
}
|
||||
return f.call();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, R> FuncN<R> fromFunc(final Func1<T0, R> f) {
|
||||
return new FuncN<R>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public R call(Object... args) {
|
||||
if (args.length != 1) {
|
||||
throw new RuntimeException("Func1 expecting 1 argument.");
|
||||
}
|
||||
return f.call((T0) args[0]);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, R> FuncN<R> fromFunc(final Func2<T0, T1, R> f) {
|
||||
return new FuncN<R>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public R call(Object... args) {
|
||||
if (args.length != 2) {
|
||||
throw new RuntimeException("Func2 expecting 2 arguments.");
|
||||
}
|
||||
return f.call((T0) args[0], (T1) args[1]);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, R> FuncN<R> fromFunc(final Func3<T0, T1, T2, R> f) {
|
||||
return new FuncN<R>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public R call(Object... args) {
|
||||
if (args.length != 3) {
|
||||
throw new RuntimeException("Func3 expecting 3 arguments.");
|
||||
}
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2]);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, R> FuncN<R> fromFunc(final Func4<T0, T1, T2, T3, R> f) {
|
||||
return new FuncN<R>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public R call(Object... args) {
|
||||
if (args.length != 4) {
|
||||
throw new RuntimeException("Func4 expecting 4 arguments.");
|
||||
}
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3]);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, T4, R> FuncN<R> fromFunc(final Func5<T0, T1, T2, T3, T4, R> f) {
|
||||
return new FuncN<R>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public R call(Object... args) {
|
||||
if (args.length != 5) {
|
||||
throw new RuntimeException("Func5 expecting 5 arguments.");
|
||||
}
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4]);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, T4, T5, R> FuncN<R> fromFunc(final Func6<T0, T1, T2, T3, T4, T5, R> f) {
|
||||
return new FuncN<R>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public R call(Object... args) {
|
||||
if (args.length != 6) {
|
||||
throw new RuntimeException("Func6 expecting 6 arguments.");
|
||||
}
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5]);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, T4, T5, T6, R> FuncN<R> fromFunc(final Func7<T0, T1, T2, T3, T4, T5, T6, R> f) {
|
||||
return new FuncN<R>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public R call(Object... args) {
|
||||
if (args.length != 7) {
|
||||
throw new RuntimeException("Func7 expecting 7 arguments.");
|
||||
}
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5], (T6) args[6]);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, T4, T5, T6, T7, R> FuncN<R> fromFunc(final Func8<T0, T1, T2, T3, T4, T5, T6, T7, R> f) {
|
||||
return new FuncN<R>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public R call(Object... args) {
|
||||
if (args.length != 8) {
|
||||
throw new RuntimeException("Func8 expecting 8 arguments.");
|
||||
}
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5], (T6) args[6], (T7) args[7]);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2, T3, T4, T5, T6, T7, T8, R> FuncN<R> fromFunc(final Func9<T0, T1, T2, T3, T4, T5, T6, T7, T8, R> f) {
|
||||
return new FuncN<R>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public R call(Object... args) {
|
||||
if (args.length != 9) {
|
||||
throw new RuntimeException("Func9 expecting 9 arguments.");
|
||||
}
|
||||
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5], (T6) args[6], (T7) args[7], (T8) args[8]);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static FuncN<Void> fromAction(final Action0 f) {
|
||||
return new FuncN<Void>() {
|
||||
|
||||
@Override
|
||||
public Void call(Object... args) {
|
||||
if (args.length != 0) {
|
||||
throw new RuntimeException("Action0 expecting 0 arguments.");
|
||||
}
|
||||
f.call();
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0> FuncN<Void> fromAction(final Action1<T0> f) {
|
||||
return new FuncN<Void>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Void call(Object... args) {
|
||||
if (args.length != 1) {
|
||||
throw new RuntimeException("Action1 expecting 1 argument.");
|
||||
}
|
||||
f.call((T0) args[0]);
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1> FuncN<Void> fromAction(final Action2<T0, T1> f) {
|
||||
return new FuncN<Void>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Void call(Object... args) {
|
||||
if (args.length != 2) {
|
||||
throw new RuntimeException("Action3 expecting 2 arguments.");
|
||||
}
|
||||
f.call((T0) args[0], (T1) args[1]);
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
|
||||
*
|
||||
* @param f
|
||||
* @return {@link FuncN}
|
||||
*/
|
||||
public static <T0, T1, T2> FuncN<Void> fromAction(final Action3<T0, T1, T2> f) {
|
||||
return new FuncN<Void>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Void call(Object... args) {
|
||||
if (args.length != 3) {
|
||||
throw new RuntimeException("Action3 expecting 3 arguments.");
|
||||
}
|
||||
f.call((T0) args[0], (T1) args[1], (T2) args[2]);
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Func1<T, Boolean> alwaysTrue() {
|
||||
return (Func1<T, Boolean>) AlwaysTrue.INSTANCE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Func1<T, T> identity() {
|
||||
return (Func1<T, T>) Identity.INSTANCE;
|
||||
}
|
||||
|
||||
private enum AlwaysTrue implements Func1<Object, Boolean> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Boolean call(Object o) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private enum Identity implements Func1<Object, Object> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Object call(Object o) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user