diff --git a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyCreateWrapper.java b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyCreateWrapper.java new file mode 100644 index 0000000000..e1f4bb0306 --- /dev/null +++ b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyCreateWrapper.java @@ -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.lang.groovy; + +import groovy.lang.Closure; +import rx.Operator; +import rx.Subscription; +import rx.util.functions.Action1; + +public class GroovyCreateWrapper implements Action1> { + + private final Closure closure; + + public GroovyCreateWrapper(Closure closure) { + this.closure = closure; + } + + @Override + public void call(Operator op) { + Object o = closure.call(op); + /* + * If the new signature is being used, we will get NULL back. + * If the old is being used we will get a Subscription back. + */ + if (o != null) { + op.add((Subscription) o); + } + } + +} diff --git a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyExtensionModule.java b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyExtensionModule.java index a4cf011d85..e4c1734bee 100644 --- a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyExtensionModule.java +++ b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyExtensionModule.java @@ -16,21 +16,16 @@ package rx.lang.groovy; import groovy.lang.Closure; -import groovy.lang.GroovySystem; import groovy.lang.MetaMethod; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Properties; import org.codehaus.groovy.reflection.CachedClass; import org.codehaus.groovy.reflection.ReflectionCache; import org.codehaus.groovy.runtime.m12n.ExtensionModule; -import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl; import rx.Observable; import rx.Observable.OnSubscribeFunc; @@ -75,6 +70,9 @@ public List getMetaMethods() { } private MetaMethod createMetaMethod(final Method m) { + if (m.getDeclaringClass().equals(Observable.class) && m.getName().equals("create")) { + return specialCasedOverrideForCreate(m); + } return new MetaMethod() { @Override @@ -109,12 +107,11 @@ public Object invoke(Object object, Object[] arguments) { if (o instanceof Closure) { if (Action.class.isAssignableFrom(m.getParameterTypes()[i])) { newArgs[i] = new GroovyActionWrapper((Closure) o); - } else if(OnSubscribeFunc.class.isAssignableFrom(m.getParameterTypes()[i])) { + } else if (OnSubscribeFunc.class.isAssignableFrom(m.getParameterTypes()[i])) { newArgs[i] = new GroovyOnSubscribeFuncWrapper((Closure) o); } else { newArgs[i] = new GroovyFunctionWrapper((Closure) o); } - } else { newArgs[i] = o; } @@ -152,4 +149,55 @@ public CachedClass[] getParameterTypes() { } }; } + + /** + * Special case until we finish migrating off the deprecated 'create' method signature + */ + private MetaMethod specialCasedOverrideForCreate(final Method m) { + return new MetaMethod() { + + @Override + public int getModifiers() { + return m.getModifiers(); + } + + @Override + public String getName() { + return m.getName(); + } + + @Override + public Class getReturnType() { + return m.getReturnType(); + } + + @Override + public CachedClass getDeclaringClass() { + return ReflectionCache.getCachedClass(m.getDeclaringClass()); + } + + @Override + public Object invoke(Object object, final Object[] arguments) { + return Observable.create(new GroovyCreateWrapper((Closure) arguments[0])); + } + + @SuppressWarnings("rawtypes") + @Override + public CachedClass[] getParameterTypes() { + Class[] pts = m.getParameterTypes(); + CachedClass[] cc = new CachedClass[pts.length]; + for (int i = 0; i < pts.length; i++) { + if (Function.class.isAssignableFrom(pts[i])) { + // function type to be replaced by closure + cc[i] = ReflectionCache.getCachedClass(Closure.class); + } else { + // non-function type + cc[i] = ReflectionCache.getCachedClass(pts[i]); + } + } + return cc; + } + }; + } + } diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala index def1f13e19..13b20cc1e5 100644 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala @@ -310,9 +310,8 @@ trait Observable[+T] * their index. Indices start at 0. */ def zipWithIndex: Observable[(T, Int)] = { - val fScala: (T, Integer) => (T, Int) = (elem: T, index: Integer) => (elem, index) - val fJava : Func2[_ >: T, Integer, _ <: (T, Int)] = fScala - toScalaObservable[(T, Int)](asJavaObservable.mapWithIndex[(T, Int)](fJava)) + var n = 0; + this.map(x => { val result = (x,n); n += 1; result }) } /** diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 18335c8953..9e3fceb71b 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -39,7 +39,6 @@ import rx.operators.OperationAverage; import rx.operators.OperationBuffer; import rx.operators.OperationCache; -import rx.operators.OperationCast; import rx.operators.OperationCombineLatest; import rx.operators.OperationConcat; import rx.operators.OperationDebounce; @@ -54,15 +53,12 @@ import rx.operators.OperationFilter; import rx.operators.OperationFinally; import rx.operators.OperationFlatMap; -import rx.operators.OperationGroupBy; import rx.operators.OperationGroupByUntil; import rx.operators.OperationGroupJoin; import rx.operators.OperationInterval; import rx.operators.OperationJoin; import rx.operators.OperationJoinPatterns; -import rx.operators.OperationMap; import rx.operators.OperationMaterialize; -import rx.operators.OperationMerge; import rx.operators.OperationMergeDelayError; import rx.operators.OperationMinMax; import rx.operators.OperationMulticast; @@ -71,7 +67,6 @@ import rx.operators.OperationOnErrorResumeNextViaObservable; import rx.operators.OperationOnErrorReturn; import rx.operators.OperationOnExceptionResumeNextViaObservable; -import rx.operators.OperationParallel; import rx.operators.OperationParallelMerge; import rx.operators.OperationRepeat; import rx.operators.OperationReplay; @@ -88,7 +83,6 @@ import rx.operators.OperationSum; import rx.operators.OperationSwitch; import rx.operators.OperationSynchronize; -import rx.operators.OperationTake; import rx.operators.OperationTakeLast; import rx.operators.OperationTakeUntil; import rx.operators.OperationTakeWhile; @@ -96,16 +90,23 @@ import rx.operators.OperationTimeInterval; import rx.operators.OperationTimeout; import rx.operators.OperationTimer; -import rx.operators.OperationTimestamp; import rx.operators.OperationToMap; import rx.operators.OperationToMultimap; import rx.operators.OperationToObservableFuture; -import rx.operators.OperationToObservableIterable; -import rx.operators.OperationToObservableList; -import rx.operators.OperationToObservableSortedList; import rx.operators.OperationUsing; import rx.operators.OperationWindow; import rx.operators.OperationZip; +import rx.operators.OperatorCast; +import rx.operators.OperatorFromIterable; +import rx.operators.OperatorGroupBy; +import rx.operators.OperatorMap; +import rx.operators.OperatorMerge; +import rx.operators.OperatorParallel; +import rx.operators.OperatorTake; +import rx.operators.OperatorTakeTimed; +import rx.operators.OperatorTimestamp; +import rx.operators.OperatorToObservableList; +import rx.operators.OperatorToObservableSortedList; import rx.operators.SafeObservableSubscription; import rx.operators.SafeObserver; import rx.plugins.RxJavaObservableExecutionHook; @@ -116,6 +117,7 @@ import rx.subjects.PublishSubject; import rx.subjects.ReplaySubject; import rx.subjects.Subject; +import rx.subscriptions.CompositeSubscription; import rx.subscriptions.Subscriptions; import rx.util.OnErrorNotImplementedException; import rx.util.Range; @@ -157,36 +159,126 @@ */ public class Observable { + final Action1> f; + + /** + * Observable with Function to execute when subscribed to. + *

+ * Note: Use {@link #create(OnSubscribeFunc)} to create an Observable, instead of this + * constructor, unless you specifically have a need for inheritance. + * + * @param onSubscribe + * {@link OnSubscribeFunc} to be executed when {@link #subscribe(Observer)} is called + */ + protected Observable(Action1> f) { + this.f = f; + } + /** - * Function interface for work to be performed when an Observable is subscribed to via - * {@link #subscribe(Observer)} + * Function interface for work to be performed when an {@link Observable} is subscribed to via {@link Observable#subscribe(Observer)} * * @param + * @deprecated */ + @Deprecated public static interface OnSubscribeFunc extends Function { public Subscription onSubscribe(Observer t1); } + private final static RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook(); + /** - * Executed when 'subscribe' is invoked. + * Returns an Observable that will execute the specified function when an {@link Observer} subscribes to it. + *

+ * + *

+ * Write the function you pass to {@code create} so that it behaves as an Observable: It should + * invoke the Observer's {@link Observer#onNext onNext}, {@link Observer#onError onError}, and {@link Observer#onCompleted onCompleted} methods appropriately. + *

+ * A well-formed Observable must invoke either the Observer's {@code onCompleted} method + * exactly once or its {@code onError} method exactly once. + *

+ * See Rx Design Guidelines (PDF) + * for detailed information. + * + * @param + * the type of the items that this Observable emits + * @param func + * a function that accepts an {@code Observer}, invokes its {@code onNext}, {@code onError}, and {@code onCompleted} methods as appropriate, and returns a {@link Subscription} that + * allows the Observer to cancel the subscription + * @return an Observable that, when an {@link Observer} subscribes to it, will execute the + * specified function + * @see RxJava Wiki: create() + * @see MSDN: Observable.Create */ - private final OnSubscribeFunc onSubscribe; + public final static Observable create(final Action1> f) { + return new Observable(f); + } - private final static RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook(); + /** + * Returns an Observable that will execute the specified function when an {@link Observer} subscribes to it. + *

+ * + *

+ * Write the function you pass to {@code create} so that it behaves as an Observable: It should + * invoke the Observer's {@link Observer#onNext onNext}, {@link Observer#onError onError}, and {@link Observer#onCompleted onCompleted} methods appropriately. + *

+ * A well-formed Observable must invoke either the Observer's {@code onCompleted} method + * exactly once or its {@code onError} method exactly once. + *

+ * See Rx Design Guidelines (PDF) + * for detailed information. + * + * @param + * the type of the items that this Observable emits + * @param func + * a function that accepts an {@code Observer}, invokes its {@code onNext}, {@code onError}, and {@code onCompleted} methods as appropriate, and returns a {@link Subscription} that + * allows the Observer to cancel the subscription + * @return an Observable that, when an {@link Observer} subscribes to it, will execute the + * specified function + * @see RxJava Wiki: create() + * @see MSDN: Observable.Create + * @deprecated + */ + @Deprecated + public final static Observable create(final OnSubscribeFunc func) { + return new Observable(new Action1>() { + + @Override + public void call(Operator o) { + o.add(func.onSubscribe(o)); + } + + }); + } /** - * Observable with Function to execute when subscribed to. + * Bind a function to the current Observable and return a new Observable that when subscribed to will pass the values of the current Observable through the function. *

- * Note: Use {@link #create(OnSubscribeFunc)} to create an Observable, instead of this - * constructor, unless you specifically have a need for inheritance. + * In other words, this allows chaining operators together on an Observable for acting on the values within the Observable. + *

+ * {@code + * observable.map(...).filter(...).take(5).bind(new OperatorA()).bind(new OperatorB(...)).subscribe() + * } * - * @param onSubscribe - * {@link OnSubscribeFunc} to be executed when {@link #subscribe(Observer)} is called + * @param bind + * @return an Observable that emits values that are the result of applying the bind function to the values of the current Observable */ - protected Observable(OnSubscribeFunc onSubscribe) { - this.onSubscribe = onSubscribe; + public Observable bind(final Func1, Operator> bind) { + return new Observable(new Action1>() { + + @Override + public void call(Operator o) { + subscribe(bind.call(o)); + } + }); } + /* ****************************************************************************** + * Operators Below Here + * ****************************************************************************** + */ + /** * Mirror the one Observable in an Iterable of several Observables that first emits an item. *

@@ -725,8 +817,7 @@ public final static Observable combin * * @param observables * an Observable that emits Observables - * @return an Observable that emits items all of the items emitted by the Observables emitted by - * {@code observables}, one after the other, without interleaving them + * @return an Observable that emits items all of the items emitted by the Observables emitted by {@code observables}, one after the other, without interleaving them * @see RxJava Wiki: concat() * @see MSDN: Observable.Concat */ @@ -958,37 +1049,6 @@ public final static Observable concat(Observable t1, Observa return create(OperationConcat.concat(t1, t2, t3, t4, t5, t6, t7, t8, t9)); } - /** - * Returns an Observable that will execute the specified function when an {@link Observer} - * subscribes to it. - *

- * - *

- * Write the function you pass to {@code create} so that it behaves as an Observable: It should - * invoke the Observer's {@link Observer#onNext onNext}, {@link Observer#onError onError}, and - * {@link Observer#onCompleted onCompleted} methods appropriately. - *

- * A well-formed Observable must invoke either the Observer's {@code onCompleted} method - * exactly once or its {@code onError} method exactly once. - *

- * See Rx Design Guidelines (PDF) - * for detailed information. - * - * @param - * the type of the items that this Observable emits - * @param func - * a function that accepts an {@code Observer}, invokes its {@code onNext}, - * {@code onError}, and {@code onCompleted} methods as appropriate, and returns a - * {@link Subscription} that allows the Observer to cancel the subscription - * @return an Observable that, when an {@link Observer} subscribes to it, will execute the - * specified function - * @see RxJava Wiki: create() - * @see MSDN: Observable.Create - */ - public final static Observable create(OnSubscribeFunc func) { - return new Observable(func); - } - /** * Returns an Observable that calls an Observable factory to create its Observable for each new * Observer that subscribes. That is, for each subscriber, the actual Observable that subscriber @@ -1014,15 +1074,13 @@ public final static Observable defer(Func0 * * * @param * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that emits no items to the {@link Observer} but immediately invokes the - * {@link Observer}'s {@link Observer#onCompleted() onCompleted} method + * @return an Observable that emits no items to the {@link Observer} but immediately invokes the {@link Observer}'s {@link Observer#onCompleted() onCompleted} method * @see RxJava Wiki: empty() * @see MSDN: Observable.Empty */ @@ -1031,8 +1089,7 @@ public final static Observable empty() { } /** - * Returns an Observable that emits no items to the {@link Observer} and immediately invokes its - * {@link Observer#onCompleted onCompleted} method on the specified scheduler. + * Returns an Observable that emits no items to the {@link Observer} and immediately invokes its {@link Observer#onCompleted onCompleted} method on the specified scheduler. *

* * @@ -1040,8 +1097,7 @@ public final static Observable empty() { * the scheduler to use to call the {@link Observer#onCompleted onCompleted} method * @param * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that emits no items to the {@link Observer} but immediately invokes the - * {@link Observer}'s {@link Observer#onCompleted() onCompleted} method with the + * @return an Observable that emits no items to the {@link Observer} but immediately invokes the {@link Observer}'s {@link Observer#onCompleted() onCompleted} method with the * specified {@code scheduler} * @see RxJava Wiki: empty() * @see MSDN: Observable.Empty Method (IScheduler) @@ -1051,8 +1107,7 @@ public final static Observable empty(Scheduler scheduler) { } /** - * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} - * method when the Observer subscribes to it. + * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it. *

* * @@ -1060,8 +1115,7 @@ public final static Observable empty(Scheduler scheduler) { * the particular Throwable to pass to {@link Observer#onError onError} * @param * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} - * method when the Observer subscribes to it + * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it * @see RxJava Wiki: error() * @see MSDN: Observable.Throw */ @@ -1070,8 +1124,7 @@ public final static Observable error(Throwable exception) { } /** - * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} - * method on the specified scheduler. + * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method on the specified scheduler. *

* * @@ -1081,8 +1134,7 @@ public final static Observable error(Throwable exception) { * the scheduler on which to call {@link Observer#onError onError} * @param * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} - * method, on the specified scheduler + * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method, on the specified scheduler * @see RxJava Wiki: error() * @see MSDN: Observable.Throw */ @@ -1153,8 +1205,7 @@ public final static Observable from(Future future, long time * @param future * the source {@link Future} * @param scheduler - * the {@link Scheduler} to wait for the Future on. Use a Scheduler such as - * {@link Schedulers#threadPoolForIO()} that can block and wait on the future. + * the {@link Scheduler} to wait for the Future on. Use a Scheduler such as {@link Schedulers#threadPoolForIO()} that can block and wait on the future. * @param * the type of object that the {@link Future} returns, and also the type of item to * be emitted by the resulting Observable @@ -1171,8 +1222,7 @@ public final static Observable from(Future future, Scheduler *

* *

- * Note: the entire iterable sequence is immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is returned, it is not possible + * Note: the entire iterable sequence is immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, it is not possible * to unsubscribe from the sequence before it completes. * * @param iterable @@ -1184,7 +1234,7 @@ public final static Observable from(Future future, Scheduler * @see RxJava Wiki: from() */ public final static Observable from(Iterable iterable) { - return from(iterable, Schedulers.immediate()); + return create(new OperatorFromIterable(iterable)); } /** @@ -1206,7 +1256,7 @@ public final static Observable from(Iterable iterable) { * @see MSDN: Observable.ToObservable */ public final static Observable from(Iterable iterable, Scheduler scheduler) { - return create(OperationToObservableIterable.toObservableIterable(iterable, scheduler)); + return create(new OperatorFromIterable(iterable)).subscribeOn(scheduler); } /** @@ -1225,7 +1275,6 @@ public final static Observable from(Iterable iterable, Sched * @return an Observable that emits the item * @see RxJava Wiki: from() */ - @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method public final static Observable from(T t1) { return from(Arrays.asList(t1)); @@ -1251,7 +1300,6 @@ public final static Observable from(T t1) { * @deprecated Use {@link #from(Iterable)} instead such as {@code from(Arrays.asList(t1))} */ @Deprecated - @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method public final static Observable from(T t1, T t2) { return from(Arrays.asList(t1, t2)); @@ -1279,7 +1327,6 @@ public final static Observable from(T t1, T t2) { * @deprecated Use {@link #from(Iterable)} instead such as {@code from(Arrays.asList(t1))}. */ @Deprecated - @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method public final static Observable from(T t1, T t2, T t3) { return from(Arrays.asList(t1, t2, t3)); @@ -1309,7 +1356,6 @@ public final static Observable from(T t1, T t2, T t3) { * @deprecated Use {@link #from(Iterable)} instead such as {@code from(Arrays.asList(t1))}. */ @Deprecated - @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method public final static Observable from(T t1, T t2, T t3, T t4) { return from(Arrays.asList(t1, t2, t3, t4)); @@ -1341,7 +1387,6 @@ public final static Observable from(T t1, T t2, T t3, T t4) { * @deprecated Use {@link #from(Iterable)} instead such as {@code from(Arrays.asList(t1))}. */ @Deprecated - @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method public final static Observable from(T t1, T t2, T t3, T t4, T t5) { return from(Arrays.asList(t1, t2, t3, t4, t5)); @@ -1375,7 +1420,6 @@ public final static Observable from(T t1, T t2, T t3, T t4, T t5) { * @deprecated Use {@link #from(Iterable)} instead such as {@code from(Arrays.asList(t1))}. */ @Deprecated - @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { return from(Arrays.asList(t1, t2, t3, t4, t5, t6)); @@ -1411,7 +1455,6 @@ public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6) { * @deprecated Use {@link #from(Iterable)} instead such as {@code from(Arrays.asList(t1))}. */ @Deprecated - @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7) { return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7)); @@ -1449,7 +1492,6 @@ public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T * @deprecated Use {@link #from(Iterable)} instead such as {@code from(Arrays.asList(t1))}. */ @Deprecated - @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8) { return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8)); @@ -1489,7 +1531,6 @@ public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T * @deprecated Use {@link #from(Iterable)} instead such as {@code from(Arrays.asList(t1))}. */ @Deprecated - @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9) { return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8, t9)); @@ -1528,7 +1569,6 @@ public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T * @deprecated Use {@link #from(Iterable)} instead such as {@code from(Arrays.asList(t1))}. */ @Deprecated - @SuppressWarnings("unchecked") // suppress unchecked because we are using varargs inside the method public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T t7, T t8, T t9, T t10) { return from(Arrays.asList(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)); @@ -1539,8 +1579,7 @@ public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T *

* *

- * Note: the entire array is immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is returned, it is not possible + * Note: the entire array is immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, it is not possible * to unsubscribe from the sequence before it completes. * * @param items @@ -1551,8 +1590,9 @@ public final static Observable from(T t1, T t2, T t3, T t4, T t5, T t6, T * @return an Observable that emits each item in the source Array * @see RxJava Wiki: from() */ - public final static Observable from(T[] items) { - return from(Arrays.asList(items)); + // @SafeVarargs // commenting out until we figure out if we can do Java7 compilation without breaking Android for just this feature + public final static Observable from(T... t1) { + return from(Arrays.asList(t1)); } /** @@ -1561,8 +1601,7 @@ public final static Observable from(T[] items) { *

* *

- * Note: the entire array is immediately emitted each time an {@link Observer} - * subscribes. Since this occurs before the {@link Subscription} is returned, it is not + * Note: the entire array is immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, it is not * possible to unsubscribe from the sequence before it completes. * * @param items @@ -1621,11 +1660,9 @@ public final static Observable interval(long interval, TimeUnit unit, Sche *

* *

- * To convert any object into an Observable that emits that object, pass that object into the - * {@code just} method. + * To convert any object into an Observable that emits that object, pass that object into the {@code just} method. *

- * This is similar to the {@link #from(java.lang.Object[])} method, except that {@code from()} - * will convert an {@link Iterable} object into an Observable that emits each of the items in + * This is similar to the {@link #from(java.lang.Object[])} method, except that {@code from()} will convert an {@link Iterable} object into an Observable that emits each of the items in * the Iterable, one at a time, while the {@code just()} method converts an Iterable into an * Observable that emits the entire Iterable as a single item. * @@ -1791,7 +1828,7 @@ public final static Observable merge(IterableMSDN: Observable.Merge */ public final static Observable merge(Observable> source) { - return create(OperationMerge.merge(source)); + return source.bind(new OperatorMerge()); // any idea how to get these generics working?! } /** @@ -1815,7 +1852,7 @@ public final static Observable merge(ObservableMSDN: Observable.Merge */ public final static Observable merge(Observable> source, int maxConcurrent) { - return create(OperationMerge.merge(source, maxConcurrent)); + return source.bind(new OperatorMerge(maxConcurrent)); // any idea how to get these generics working?! } /** @@ -2083,16 +2120,14 @@ public final static Observable merge(Observable[] sequences, *

* *

- * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. + * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. *

* This method allows an Observer to observe all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. * * @param source * an Observable that emits Observables - * @return an Observable that emits all of the items emitted by the Observables emitted by the - * {@code source} Observable + * @return an Observable that emits all of the items emitted by the Observables emitted by the {@code source} Observable * @see RxJava Wiki: mergeDelayError() * @see MSDN: Observable.Merge */ @@ -2102,14 +2137,12 @@ public final static Observable mergeDelayError(Observable * *

- * Even if both merged Observables send {@code onError} notifications, {@code mergeDelayError} - * will only invoke the {@code onError} method of its Observers once. + * Even if both merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. *

* This method allows an Observer to receive all successfully emitted items from each of the * source Observables without being interrupted by an error notification from one of them. @@ -2131,14 +2164,12 @@ public final static Observable mergeDelayError(Observable t1 /** * This behaves like {@link #merge(Observable, Observable, Observable)} except that if any of - * the merged Observables notify of an error via {@link Observer#onError onError}, - * {@code mergeDelayError} will refrain from propagating that error notification until all of + * the merged Observables notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that error notification until all of * the merged Observables have finished emitting items. *

* *

- * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. + * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. *

* This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. @@ -2161,14 +2192,12 @@ public final static Observable mergeDelayError(Observable t1 /** * This behaves like {@link #merge(Observable, Observable, Observable, Observable)} except that - * if any of the merged Observables notify of an error via {@link Observer#onError onError}, - * {@code mergeDelayError} will refrain from propagating that error notification until all of + * if any of the merged Observables notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that error notification until all of * the merged Observables have finished emitting items. *

* *

- * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. + * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. *

* This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. @@ -2192,15 +2221,13 @@ public final static Observable mergeDelayError(Observable t1 } /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables notify of an error via {@link Observer#onError onError} + * , {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged Observables have finished emitting items. *

* *

- * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. + * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. *

* This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. @@ -2226,16 +2253,13 @@ public final static Observable mergeDelayError(Observable t1 } /** - * This behaves like - * {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables notify of an error via * {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged Observables have finished emitting items. *

* *

- * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. + * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. *

* This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. @@ -2263,15 +2287,13 @@ public final static Observable mergeDelayError(Observable t1 } /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables notify of an error via * {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged Observables have finished emitting items. *

* *

- * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. + * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. *

* This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. @@ -2301,15 +2323,13 @@ public final static Observable mergeDelayError(Observable t1 } /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables notify of an error + * via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged Observables have finished emitting items. *

* *

- * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. + * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. *

* This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. @@ -2341,15 +2361,13 @@ public final static Observable mergeDelayError(Observable t1 } /** - * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} - * except that if any of the merged Observables notify of an error via - * {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that + * This behaves like {@link #merge(Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable, Observable)} except that if any of the merged Observables notify + * of an error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that * error notification until all of the merged Observables have finished emitting items. *

* *

- * Even if multiple merged Observables send {@code onError} notifications, - * {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. + * Even if multiple merged Observables send {@code onError} notifications, {@code mergeDelayError} will only invoke the {@code onError} method of its Observers once. *

* This method allows an Observer to receive all successfully emitted items from all of the * source Observables without being interrupted by an error notification from one of them. @@ -2408,8 +2426,7 @@ public final static > Observable min(Observab * * @param * the type of items (not) emitted by the Observable - * @return an Observable that never emits any items or sends any notifications to an - * {@link Observer} + * @return an Observable that never emits any items or sends any notifications to an {@link Observer} * @see RxJava Wiki: never() */ public final static Observable never() { @@ -2417,12 +2434,10 @@ public final static Observable never() { } /** - * Converts an {@code Observable>} into another {@code Observable>} - * whose emitted Observables emit the same items, but the number of such Observables is + * Converts an {@code Observable>} into another {@code Observable>} whose emitted Observables emit the same items, but the number of such Observables is * restricted by {@code parallelObservables}. *

- * For example, if the original {@code Observable>} emits 100 Observables and - * {@code parallelObservables} is 8, the items emitted by the 100 original Observables will be + * For example, if the original {@code Observable>} emits 100 Observables and {@code parallelObservables} is 8, the items emitted by the 100 original Observables will be * distributed among 8 Observables emitted by the resulting Observable. *

* @@ -2440,12 +2455,10 @@ public final static Observable> parallelMerge(Observable>} into another {@code Observable>} - * whose emitted Observables emit the same items, but the number of such Observables is + * Converts an {@code Observable>} into another {@code Observable>} whose emitted Observables emit the same items, but the number of such Observables is * restricted by {@code parallelObservables}, and each runs on a defined Scheduler. *

- * For example, if the original {@code Observable>} emits 100 Observables and - * {@code parallelObservables} is 8, the items emitted by the 100 original Observables will be + * For example, if the original {@code Observable>} emits 100 Observables and {@code parallelObservables} is 8, the items emitted by the 100 original Observables will be * distributed among 8 Observables emitted by the resulting Observable. *

* @@ -2702,7 +2715,7 @@ public final static Observable synchronize(Observable source) { * @see MSDN: Observable.Timer */ public final static Observable timer(long initialDelay, long period, TimeUnit unit) { - return timer(initialDelay, period, unit, Schedulers.threadPoolForComputation()); + return timer(initialDelay, period, unit, Schedulers.computation()); } /** @@ -2720,8 +2733,7 @@ public final static Observable timer(long initialDelay, long period, TimeU * @param scheduler * the scheduler on which the waiting happens and items are emitted * @return an Observable that emits a 0L after the {@code initialDelay} and ever increasing - * numbers after each {@code period} of time thereafter, while running on the given - * {@code scheduler} + * numbers after each {@code period} of time thereafter, while running on the given {@code scheduler} * @see RxJava Wiki: timer() * @see MSDN: Observable.Timer */ @@ -2741,7 +2753,7 @@ public final static Observable timer(long initialDelay, long period, TimeU * @see RxJava wiki: timer() */ public final static Observable timer(long delay, TimeUnit unit) { - return timer(delay, unit, Schedulers.threadPoolForComputation()); + return timer(delay, unit, Schedulers.computation()); } /** @@ -3035,8 +3047,7 @@ public final static Observable when(Plan0 p1, Plan0 p2, Plan0 p3 /** * Returns an Observable that emits the results of a function of your choosing applied to * combinations items emitted, in sequence, by an Iterable of other Observables. - *

- * {@code zip} applies this function in strict sequence, so the first item emitted by the new + *

{@code zip} applies this function in strict sequence, so the first item emitted by the new * Observable will be the result of the function applied to the first item emitted by each of * the source Observables; the second item emitted by the new Observable will be the result of * the function applied to the second item emitted by each of those Observables; and so forth. @@ -3063,8 +3074,7 @@ public final static Observable zip(Iterable> ws, * Returns an Observable that emits the results of a function of your choosing applied to * combinations of n items emitted, in sequence, by the n Observables emitted by * a specified Observable. - *

- * {@code zip} applies this function in strict sequence, so the first item emitted by the new + *

{@code zip} applies this function in strict sequence, so the first item emitted by the new * Observable will be the result of the function applied to the first item emitted by each of * the Observables emitted by the source Observable; the second item emitted by the new * Observable will be the result of the function applied to the second item emitted by each of @@ -3099,15 +3109,12 @@ public final Observable call(List> wsList) { * combinations of two items emitted, in sequence, by two other Observables. *

* - *

- * {@code zip} applies this function in strict sequence, so the first item emitted by the new - * Observable will be the result of the function applied to the first item emitted by {@code o1} - * and the first item emitted by {@code o2}; the second item emitted by the new Observable will + *

{@code zip} applies this function in strict sequence, so the first item emitted by the new + * Observable will be the result of the function applied to the first item emitted by {@code o1} and the first item emitted by {@code o2}; the second item emitted by the new Observable will * be the result of the function applied to the second item emitted by {@code o1} and the second * item emitted by {@code o2}; and so forth. *

- * The resulting {@code Observable} returned from {@code zip} will invoke - * {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of * the source Observable that emits the fewest items. * * @param o1 @@ -3129,16 +3136,13 @@ public final static Observable zip(Observable o1, O * combinations of three items emitted, in sequence, by three other Observables. *

* - *

- * {@code zip} applies this function in strict sequence, so the first item emitted by the new - * Observable will be the result of the function applied to the first item emitted by - * {@code o1}, the first item emitted by {@code o2}, and the first item emitted by {@code o3}; + *

{@code zip} applies this function in strict sequence, so the first item emitted by the new + * Observable will be the result of the function applied to the first item emitted by {@code o1}, the first item emitted by {@code o2}, and the first item emitted by {@code o3}; * the second item emitted by the new Observable will be the result of the function applied to * the second item emitted by {@code o1}, the second item emitted by {@code o2}, and the second * item emitted by {@code o3}; and so forth. *

- * The resulting {@code Observable} returned from {@code zip} will invoke - * {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of * the source Observable that emits the fewest items. * * @param o1 @@ -3162,16 +3166,13 @@ public final static Observable zip(Observable o * combinations of four items emitted, in sequence, by four other Observables. *

* - *

- * {@code zip} applies this function in strict sequence, so the first item emitted by the new - * Observable will be the result of the function applied to the first item emitted by - * {@code o1}, the first item emitted by {@code o2}, the first item emitted by {@code o3}, and + *

{@code zip} applies this function in strict sequence, so the first item emitted by the new + * Observable will be the result of the function applied to the first item emitted by {@code o1}, the first item emitted by {@code o2}, the first item emitted by {@code o3}, and * the first item emitted by {@code 04}; the second item emitted by the new Observable will be * the result of the function applied to the second item emitted by each of those Observables; * and so forth. *

- * The resulting {@code Observable} returned from {@code zip} will invoke - * {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of * the source Observable that emits the fewest items. * * @param o1 @@ -3197,16 +3198,13 @@ public final static Observable zip(Observable * - *

- * {@code zip} applies this function in strict sequence, so the first item emitted by the new - * Observable will be the result of the function applied to the first item emitted by - * {@code o1}, the first item emitted by {@code o2}, the first item emitted by {@code o3}, the + *

{@code zip} applies this function in strict sequence, so the first item emitted by the new + * Observable will be the result of the function applied to the first item emitted by {@code o1}, the first item emitted by {@code o2}, the first item emitted by {@code o3}, the * first item emitted by {@code o4}, and the first item emitted by {@code o5}; the second item * emitted by the new Observable will be the result of the function applied to the second item * emitted by each of those Observables; and so forth. *

- * The resulting {@code Observable} returned from {@code zip} will invoke - * {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of * the source Observable that emits the fewest items. * * @param o1 @@ -3234,14 +3232,12 @@ public final static Observable zip(Observable * - *

- * {@code zip} applies this function in strict sequence, so the first item emitted by the new + *

{@code zip} applies this function in strict sequence, so the first item emitted by the new * Observable will be the result of the function applied to the first item emitted each source * Observable, the second item emitted by the new Observable will be the result of the function * applied to the second item emitted by each of those Observables, and so forth. *

- * The resulting {@code Observable} returned from {@code zip} will invoke - * {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of * the source Observable that emits the fewest items. * * @param o1 @@ -3272,14 +3268,12 @@ public final static Observable zip(Observable * - *

- * {@code zip} applies this function in strict sequence, so the first item emitted by the new + *

{@code zip} applies this function in strict sequence, so the first item emitted by the new * Observable will be the result of the function applied to the first item emitted each source * Observable, the second item emitted by the new Observable will be the result of the function * applied to the second item emitted by each of those Observables, and so forth. *

- * The resulting {@code Observable} returned from {@code zip} will invoke - * {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of * the source Observable that emits the fewest items. * * @param o1 @@ -3312,14 +3306,12 @@ public final static Observable zip(Observable * combinations of eight items emitted, in sequence, by eight other Observables. *

* - *

- * {@code zip} applies this function in strict sequence, so the first item emitted by the new + *

{@code zip} applies this function in strict sequence, so the first item emitted by the new * Observable will be the result of the function applied to the first item emitted each source * Observable, the second item emitted by the new Observable will be the result of the function * applied to the second item emitted by each of those Observables, and so forth. *

- * The resulting {@code Observable} returned from {@code zip} will invoke - * {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of * the source Observable that emits the fewest items. * * @param o1 @@ -3354,14 +3346,12 @@ public final static Observable zip(Observ * combinations of nine items emitted, in sequence, by nine other Observables. *

* - *

- * {@code zip} applies this function in strict sequence, so the first item emitted by the new + *

{@code zip} applies this function in strict sequence, so the first item emitted by the new * Observable will be the result of the function applied to the first item emitted each source * Observable, the second item emitted by the new Observable will be the result of the function * applied to the second item emitted by each of those Observables, and so forth. *

- * The resulting {@code Observable} returned from {@code zip} will invoke - * {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} as many times as the number of {@code onNext} invocations of * the source Observable that emits the fewest items. * * @param o1 @@ -3544,9 +3534,8 @@ public final Observable averageLong(Func1 valueExtractor) /** * Returns an Observable that emits buffers of items it collects from the source Observable. * The resulting Observable emits connected, non-overlapping buffers. It emits the current - * buffer and replaces it with a new buffer when the Observable produced by the specified - * {@code bufferClosingSelector} emits an item. It then uses the {@code bufferClosingSelector} - * to create a new Observable to observe to indicate the end of the next buffer. + * buffer and replaces it with a new buffer when the Observable produced by the specified {@code bufferClosingSelector} emits an item. It then uses the {@code bufferClosingSelector} to create a + * new Observable to observe to indicate the end of the next buffer. *

* * @@ -3555,8 +3544,7 @@ public final Observable averageLong(Func1 valueExtractor) * this {@code Observable} emits an item, {@code buffer()} emits the associated * buffer and replaces it with a new one. * @return an Observable that emits a connected, non-overlapping buffer of items from - * the source Observable each time the current Observable created with the - * {@code bufferClosingSelector} argument emits an item + * the source Observable each time the current Observable created with the {@code bufferClosingSelector} argument emits an item * @see RxJava Wiki: buffer() */ public final Observable> buffer(Func0> bufferClosingSelector) { @@ -3565,8 +3553,7 @@ public final Observable> buffer(Func0 @@ -3584,8 +3571,7 @@ public final Observable> buffer(int count) { /** * Returns an Observable that emits buffers of items it collects from the source Observable. - * The resulting Observable emits buffers every {@code skip} items, each containing - * {@code count} items. When the source Observable completes or encounters an error, the + * The resulting Observable emits buffers every {@code skip} items, each containing {@code count} items. When the source Observable completes or encounters an error, the * resulting Observable emits the current buffer and propagates the notification from the source * Observable. *

@@ -3607,9 +3593,8 @@ public final Observable> buffer(int count, int skip) { /** * Returns an Observable that emits buffers of items it collects from the source Observable. - * The resulting Observable starts a new buffer periodically, as determined by the - * {@code timeshift} argument. It emits each buffer after a fixed timespan, specified by the - * {@code timespan} argument. When the source Observable completes or encounters an error, the + * The resulting Observable starts a new buffer periodically, as determined by the {@code timeshift} argument. It emits each buffer after a fixed timespan, specified by the {@code timespan} + * argument. When the source Observable completes or encounters an error, the * resulting Observable emits the current buffer and propagates the notification from the source * Observable. *

@@ -3620,8 +3605,7 @@ public final Observable> buffer(int count, int skip) { * @param timeshift * the period of time after which a new buffer will be created * @param unit - * the unit of time that applies to the {@code timespan} and {@code timeshift} - * arguments + * the unit of time that applies to the {@code timespan} and {@code timeshift} arguments * @return an Observable that emits new buffers of items emitted by the source * Observable periodically after a fixed timespan has elapsed * @see RxJava Wiki: buffer() @@ -3632,8 +3616,7 @@ public final Observable> buffer(long timespan, long timeshift, TimeUnit /** * Returns an Observable that emits buffers of items it collects from the source Observable. - * The resulting Observable starts a new buffer periodically, as determined by the - * {@code timeshift} argument, and on the specified {@code scheduler}. It emits each buffer + * The resulting Observable starts a new buffer periodically, as determined by the {@code timeshift} argument, and on the specified {@code scheduler}. It emits each buffer * after a fixed timespan, specified by the {@code timespan} argument. When the source * Observable completes or encounters an error, the resulting Observable emits the current * buffer propagates the notification from the source Observable. @@ -3645,8 +3628,7 @@ public final Observable> buffer(long timespan, long timeshift, TimeUnit * @param timeshift * the period of time after which a new buffer will be created * @param unit - * the unit of time that applies to the {@code timespan} and {@code timeshift} - * arguments + * the unit of time that applies to the {@code timespan} and {@code timeshift} arguments * @param scheduler * the {@link Scheduler} to use when determining the end and start of a buffer * @return an Observable that emits new buffers of items emitted by the source @@ -3682,8 +3664,7 @@ public final Observable> buffer(long timespan, TimeUnit unit) { /** * Returns an Observable that emits buffers of items it collects from the source Observable. * The resulting Observable emits connected, non-overlapping buffers, each of a fixed duration - * specified by the {@code timespan} argument or a maximum size specified by the {@code count} - * argument (whichever is reached first). When the source Observable completes or encounters an + * specified by the {@code timespan} argument or a maximum size specified by the {@code count} argument (whichever is reached first). When the source Observable completes or encounters an * error, the resulting Observable emits the current buffer and propagates the notification from * the source Observable. *

@@ -3759,8 +3740,7 @@ public final Observable> buffer(long timespan, TimeUnit unit, Scheduler /** * Returns an Observable that emits buffers of items it collects from the source Observable. - * The resulting Observable emits buffers that it creates when the specified - * {@code bufferOpenings} Observable emits an item, and closes when the Observable returned from + * The resulting Observable emits buffers that it creates when the specified {@code bufferOpenings} Observable emits an item, and closes when the Observable returned from * {@code bufferClosingSelector} emits an item. *

* @@ -3837,11 +3817,9 @@ public final Observable> buffer(Observable boundary, int initialC * subscribe/unsubscribe behavior of all the {@link Observer}s. *

* When you call {@code cache()}, it does not yet subscribe to the source Observable. This only - * happens when {@code subscribe} is called the first time on the Observable returned by - * {@code cache()}. + * happens when {@code subscribe} is called the first time on the Observable returned by {@code cache()}. *

- * Note: You sacrifice the ability to unsubscribe from the origin when you use the - * {@code cache()} operator so be careful not to use this operator on Observables that emit an + * Note: You sacrifice the ability to unsubscribe from the origin when you use the {@code cache()} operator so be careful not to use this operator on Observables that emit an * infinite or very large number of items that will use up memory. * * @return an Observable that, when first subscribed to, caches all of its items and @@ -3867,7 +3845,7 @@ public final Observable cache() { * @see MSDN: Observable.Cast */ public final Observable cast(final Class klass) { - return create(OperationCast.cast(this, klass)); + return bind(new OperatorCast(klass)); } /** @@ -3877,8 +3855,10 @@ public final Observable cast(final Class klass) { * pass. *

* - * @param state FIXME FIXME FIXME - * @param collector FIXME FIXME FIXME + * @param state + * FIXME FIXME FIXME + * @param collector + * FIXME FIXME FIXME * @return FIXME FIXME FIXME */ public final Observable collect(R state, final Action2 collector) { @@ -4063,8 +4043,7 @@ public final Observable defaultIfEmpty(T defaultValue) { *

* *

- * Note: the resulting Observable will immediately propagate any {@code onError} - * notification from the source Observable. + * Note: the resulting Observable will immediately propagate any {@code onError} notification from the source Observable. * * @param * the subscription delay value type (ignored) @@ -4092,8 +4071,7 @@ public final Observable delay( *

* *

- * Note: the resulting Observable will immediately propagate any {@code onError} - * notification from the source Observable. + * Note: the resulting Observable will immediately propagate any {@code onError} notification from the source Observable. * * @param * the item delay value type (ignored) @@ -4123,7 +4101,7 @@ public final Observable delay(Func1> i * @see MSDN: Observable.Delay */ public final Observable delay(long delay, TimeUnit unit) { - return OperationDelay.delay(this, delay, unit, Schedulers.threadPoolForComputation()); + return OperationDelay.delay(this, delay, unit, Schedulers.computation()); } /** @@ -4160,7 +4138,7 @@ public final Observable delay(long delay, TimeUnit unit, Scheduler scheduler) * amount */ public final Observable delaySubscription(long delay, TimeUnit unit) { - return delaySubscription(delay, unit, Schedulers.threadPoolForComputation()); + return delaySubscription(delay, unit, Schedulers.computation()); } /** @@ -4189,8 +4167,7 @@ public final Observable delaySubscription(long delay, TimeUnit unit, Schedule *

* * - * @return an Observable that emits the items and notifications embedded in the - * {@link Notification} objects emitted by the source Observable + * @return an Observable that emits the items and notifications embedded in the {@link Notification} objects emitted by the source Observable * @throws Throwable * if the source Observable is not of type {@code Observable>} * @see RxJava Wiki: dematerialize() @@ -4452,8 +4429,7 @@ public final Observable elementAtOrDefault(int index, T defaultValue) { /** * Returns an Observable that emits {@code true} if any item emitted by the source Observable - * satisfies a specified condition, otherwise {@code false}. Note: this always emits - * {@code false} if the source Observable is empty. + * satisfies a specified condition, otherwise {@code false}. Note: this always emits {@code false} if the source Observable is empty. *

* *

@@ -4477,8 +4453,7 @@ public final Observable exists(Func1 predicate) { * * * @param predicate - * a function that evaluates the items emitted by the source Observable, returning - * {@code true} if they pass the filter + * a function that evaluates the items emitted by the source Observable, returning {@code true} if they pass the filter * @return an Observable that emits only those items emitted by the source Observable that the * filter evaluates as {@code true} * @see RxJava Wiki: filter() @@ -4488,15 +4463,13 @@ public final Observable filter(Func1 predicate) { } /** - * Registers an {@link Action0} to be called when this Observable invokes either - * {@link Observer#onCompleted onCompleted} or {@link Observer#onError onError}. + * Registers an {@link Action0} to be called when this Observable invokes either {@link Observer#onCompleted onCompleted} or {@link Observer#onError onError}. *

* * * @param action * an {@link Action0} to be invoked when the source Observable finishes - * @return an Observable that emits the same items as the source Observable, then invokes the - * {@link Action0} + * @return an Observable that emits the same items as the source Observable, then invokes the {@link Action0} * @see RxJava Wiki: finallyDo() * @see MSDN: Observable.Finally */ @@ -4529,8 +4502,7 @@ public final Observable first() { * @param predicate * the condition that an item emitted by the source Observable has to satisfy * @return an Observable that emits only the very first item emitted by the source Observable - * that satisfies the {@code predicate}, or raises an {@code IllegalArgumentException} - * if no such items are emitted + * that satisfies the {@code predicate}, or raises an {@code IllegalArgumentException} if no such items are emitted * @see RxJava Wiki: first() * @see MSDN: {@code Observable.firstAsync()} */ @@ -4615,13 +4587,12 @@ public final Observable flatMap(Func1RxJava Wiki: groupBy */ public final Observable> groupBy(final Func1 keySelector) { - return create(OperationGroupBy.groupBy(this, keySelector)); + return bind(new OperatorGroupBy(keySelector)); } /** * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items, transformed by a selector, within {@link GroupedObservable}s, one - * {@code GroupedObservable} per group. + * grouped items, transformed by a selector, within {@link GroupedObservable}s, one {@code GroupedObservable} per group. *

* * @@ -4639,7 +4610,7 @@ public final Observable> groupBy(final Func1RxJava Wiki: groupBy */ public final Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { - return create(OperationGroupBy.groupBy(this, keySelector, elementSelector)); + return null; } /** @@ -4714,8 +4685,7 @@ public final Observable groupJoin(Observable right, Func1 } /** - * Ignores all items emitted by the source Observable and only calls {@code onCompleted} or - * {@code onError}. + * Ignores all items emitted by the source Observable and only calls {@code onCompleted} or {@code onError}. *

* * @@ -4729,8 +4699,7 @@ public final Observable ignoreElements() { } /** - * Returns an Observable that emits {@code true} if the source Observable is empty, otherwise - * {@code false}. + * Returns an Observable that emits {@code true} if the source Observable is empty, otherwise {@code false}. *

* In Rx.Net this is negated as the {@code any} operator but we renamed this in RxJava to better * match Java naming idioms. @@ -4756,8 +4725,7 @@ public final Observable isEmpty() { * a function to select a duration for each item emitted by the source Observable, * used to determine overlap * @param rightDurationSelector - * a function to select a duration for each item emitted by the {@code right} - * Observable, used to determine overlap + * a function to select a duration for each item emitted by the {@code right} Observable, used to determine overlap * @param resultSelector * a function that computes an item to be emitted by the resulting Observable for any * two overlapping items emitted by the two Observables @@ -4881,7 +4849,7 @@ public final Long call(Long t1, T t2) { * @see MSDN: Observable.Select */ public final Observable map(Func1 func) { - return create(OperationMap.map(this, func)); + return bind(new OperatorMap(func)); } /** @@ -4908,26 +4876,6 @@ public final Observable mapMany(Func1 - * - * - * @param func - * a function to apply to each item emitted by the source Observable that takes the - * sequential index of the emitted item as additional parameter - * @return an Observable that emits the items from the source Observable, transformed by the - * specified function - * @see RxJava Wiki: mapWithIndex() - * @see MSDN: Observable.Select - * @deprecated just use {@code zip} with {@link #range(int)} - */ - @Deprecated - public final Observable mapWithIndex(Func2 func) { - return create(OperationMap.mapWithIndex(this, func)); - } - /** * Turns all of the emissions and notifications from a source Observable into emissions marked * with their original types within {@link Notification} objects. @@ -4955,7 +4903,7 @@ public final Observable> materialize() { * @return an Observable that emits the maximum item emitted by the source Observable, according * to the specified comparator * @throws IllegalArgumentException - * if the source is empty + * if the source is empty * @see RxJava Wiki: max() * @see MSDN: Observable.Max */ @@ -5251,14 +5199,12 @@ public final Boolean call(T t) { } /** - * Instruct an Observable to pass control to another Observable rather than invoking - * {@link Observer#onError onError} if it encounters an error. + * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error. *

* *

* By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's {@code onError} - * method, and then quits without invoking any more of its Observer's methods. The + * expected item to its {@link Observer}, the Observable invokes its Observer's {@code onError} method, and then quits without invoking any more of its Observer's methods. The * {@code onErrorResumeNext} method changes this behavior. If you pass a function that returns * an Observable ({@code resumeFunction}) to {@code onErrorResumeNext}, if the original * Observable encounters an error, instead of invoking its Observer's {@code onError} method, it @@ -5281,19 +5227,16 @@ public final Observable onErrorResumeNext(final Func1 * *

* By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's {@code onError} - * method, and then quits without invoking any more of its Observer's methods. The + * expected item to its {@link Observer}, the Observable invokes its Observer's {@code onError} method, and then quits without invoking any more of its Observer's methods. The * {@code onErrorResumeNext} method changes this behavior. If you pass another Observable * ({@code resumeSequence}) to an Observable's {@code onErrorResumeNext} method, if the original * Observable encounters an error, instead of invoking its Observer's {@code onError} method, it - * will instead relinquish control to {@code resumeSequence} which will invoke the Observer's - * {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no + * will instead relinquish control to {@code resumeSequence} which will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no * Observable necessarily invokes {@code onError}, the Observer may never know that an error * happened. *

@@ -5317,8 +5260,7 @@ public final Observable onErrorResumeNext(final Observable resum * *

* By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's {@code onError} - * method, and then quits without invoking any more of its Observer's methods. The + * expected item to its {@link Observer}, the Observable invokes its Observer's {@code onError} method, and then quits without invoking any more of its Observer's methods. The * {@code onErrorReturn} method changes this behavior. If you pass a function * ({@code resumeFunction}) to an Observable's {@code onErrorReturn} method, if the original * Observable encounters an error, instead of invoking its Observer's {@code onError} method, it @@ -5338,21 +5280,17 @@ public final Observable onErrorReturn(Func1 resumeFun } /** - * Instruct an Observable to pass control to another Observable rather than invoking - * {@link Observer#onError onError} if it encounters an {@link java.lang.Exception}. + * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an {@link java.lang.Exception}. *

- * This differs from {@link #onErrorResumeNext} in that this one does not handle - * {@link java.lang.Throwable} or {@link java.lang.Error} but lets those continue through. + * This differs from {@link #onErrorResumeNext} in that this one does not handle {@link java.lang.Throwable} or {@link java.lang.Error} but lets those continue through. *

* *

* By default, when an Observable encounters an exception that prevents it from emitting the - * expected item to its {@link Observer}, the Observable invokes its Observer's {@code onError} - * method, and then quits without invoking any more of its Observer's methods. The + * expected item to its {@link Observer}, the Observable invokes its Observer's {@code onError} method, and then quits without invoking any more of its Observer's methods. The * {@code onExceptionResumeNext} method changes this behavior. If you pass another Observable * ({@code resumeSequence}) to an Observable's {@code onExceptionResumeNext} method, if the - * original Observable encounters an exception, instead of invoking its Observer's - * {@code onError} method, it will instead relinquish control to {@code resumeSequence} which + * original Observable encounters an exception, instead of invoking its Observer's {@code onError} method, it will instead relinquish control to {@code resumeSequence} which * will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In * such a case, because no Observable necessarily invokes {@code onError}, the Observer may * never know that an exception happened. @@ -5371,9 +5309,7 @@ public final Observable onExceptionResumeNext(final Observable r } /** - * Perform work on the source {@code Observable} in parallel by sharding it on a - * {@link Schedulers#threadPoolForComputation()} {@link Scheduler}, and return the resulting - * {@code Observable}. + * Perform work on the source {@code Observable} in parallel by sharding it on a {@link Schedulers#threadPoolForComputation()} {@link Scheduler}, and return the resulting {@code Observable}. *

* * @@ -5385,12 +5321,11 @@ public final Observable onExceptionResumeNext(final Observable r * @see RxJava Wiki: parallel() */ public final Observable parallel(Func1, Observable> f) { - return OperationParallel.parallel(this, f); + return bind(new OperatorParallel(f, Schedulers.computation())); } /** - * Perform work on the source {@code Observable} in parallel by sharding it on a - * {@link Scheduler}, and return the resulting {@code Observable}. + * Perform work on the source {@code Observable} in parallel by sharding it on a {@link Scheduler}, and return the resulting {@code Observable}. *

* * @@ -5404,7 +5339,7 @@ public final Observable parallel(Func1, Observable> f) { * @see RxJava Wiki: parallel() */ public final Observable parallel(final Func1, Observable> f, final Scheduler s) { - return OperationParallel.parallel(this, f, s); + return bind(new OperatorParallel(f, s)); } /** @@ -5420,8 +5355,7 @@ private Subscription protectivelyWrapAndSubscribe(Observer o) { } /** - * Returns a {@link ConnectableObservable}, which waits until its - * {@link ConnectableObservable#connect connect} method is called before it begins emitting + * Returns a {@link ConnectableObservable}, which waits until its {@link ConnectableObservable#connect connect} method is called before it begins emitting * items to those {@link Observer}s that have subscribed to it. *

* @@ -5533,8 +5467,7 @@ public final ConnectableObservable publishLast() { * a function that can use the multicasted source sequence as many times as needed, * without causing multiple subscriptions to the source Observable. Subscribers to * the source will only receive the last item emitted by the source. - * @return an Observable that emits items that are the result of invoking the selector on a - * {@link ConnectableObservable} that shares a single subscription to the underlying + * @return an Observable that emits items that are the result of invoking the selector on a {@link ConnectableObservable} that shares a single subscription to the underlying * Observable but contains only its last emission. */ public final Observable publishLast(Func1, ? extends Observable> selector) { @@ -5563,7 +5496,7 @@ public final Subject call() { * an accumulator function to be invoked on each item emitted by the source * Observable, whose result will be used in the next accumulator call * @return an Observable that emits a single item that is the result of accumulating the items - * emitted by the source Observable + * emitted by the source Observable * @throws IllegalArgumentException * if the source Observable emits no items * @see RxJava Wiki: reduce() @@ -5643,8 +5576,7 @@ public final Observable repeat(Scheduler scheduler) { /** * Returns a {@link ConnectableObservable} that shares a single subscription to the underlying - * Observable that will replay all of its items and notifications to any future - * {@link Observer}. + * Observable that will replay all of its items and notifications to any future {@link Observer}. *

* * @@ -5668,8 +5600,7 @@ public final ConnectableObservable replay() { * @param selector * the selector function, which can use the multicasted sequence as many times as * needed, without causing multiple subscriptions to the Observable - * @return an Observable that emits items that are the results of invoking the selector on a - * {@link ConnectableObservable} that shares a single subscription to the underlying + * @return an Observable that emits items that are the results of invoking the selector on a {@link ConnectableObservable} that shares a single subscription to the underlying * Observable * @see RxJava Wiki: replay() * @see MSDN: Observable.Replay @@ -5741,7 +5672,7 @@ public final Subject call() { * @see MSDN: Observable.Replay */ public final Observable replay(Func1, ? extends Observable> selector, int bufferSize, long time, TimeUnit unit) { - return replay(selector, bufferSize, time, unit, Schedulers.threadPoolForComputation()); + return replay(selector, bufferSize, time, unit, Schedulers.computation()); } /** @@ -5841,7 +5772,7 @@ public final Subject call() { * @see MSDN: Observable.Replay */ public final Observable replay(Func1, ? extends Observable> selector, long time, TimeUnit unit) { - return replay(selector, time, unit, Schedulers.threadPoolForComputation()); + return replay(selector, time, unit, Schedulers.computation()); } /** @@ -5944,7 +5875,7 @@ public final ConnectableObservable replay(int bufferSize) { * @see MSDN: Observable.Replay */ public final ConnectableObservable replay(int bufferSize, long time, TimeUnit unit) { - return replay(bufferSize, time, unit, Schedulers.threadPoolForComputation()); + return replay(bufferSize, time, unit, Schedulers.computation()); } /** @@ -6008,13 +5939,12 @@ public final ConnectableObservable replay(int bufferSize, Scheduler scheduler * @param unit * the time unit of {@code time} * @return a {@link ConnectableObservable} that shares a single subscription to the underlying - * Observable and replays the items that were emitted during the window defined by - * {@code time} + * Observable and replays the items that were emitted during the window defined by {@code time} * @see RxJava Wiki: replay() * @see MSDN: Observable.Replay */ public final ConnectableObservable replay(long time, TimeUnit unit) { - return replay(time, unit, Schedulers.threadPoolForComputation()); + return replay(time, unit, Schedulers.computation()); } /** @@ -6030,8 +5960,7 @@ public final ConnectableObservable replay(long time, TimeUnit unit) { * @param scheduler * the scheduler that is the time source for the window * @return a {@link ConnectableObservable} that shares a single subscription to the underlying - * Observable and replays the items that were emitted during the window defined by - * {@code time} + * Observable and replays the items that were emitted during the window defined by {@code time} * @see RxJava Wiki: replay() * @see MSDN: Observable.Replay */ @@ -6041,16 +5970,14 @@ public final ConnectableObservable replay(long time, TimeUnit unit, Scheduler /** * Returns a {@link ConnectableObservable} that shares a single subscription to the underlying - * Observable that will replay all of its items and notifications to any future {@link Observer} - * on the given {@link Scheduler}. + * Observable that will replay all of its items and notifications to any future {@link Observer} on the given {@link Scheduler}. *

* * * @param scheduler * the scheduler on which the Observers will observe the emitted items * @return a {@link ConnectableObservable} that shares a single subscription to the source - * Observable that will replay all of its items and notifications to any future - * {@link Observer} on the given {@link Scheduler} + * Observable that will replay all of its items and notifications to any future {@link Observer} on the given {@link Scheduler} * @see RxJava Wiki: replay() * @see MSDN: Observable.Replay */ @@ -6059,8 +5986,7 @@ public final ConnectableObservable replay(Scheduler scheduler) { } /** - * Return an Observable that mirrors the source Observable, resubscribing to it if it calls - * {@code onError} (infinite retry count). + * Return an Observable that mirrors the source Observable, resubscribing to it if it calls {@code onError} (infinite retry count). *

* *

@@ -6069,8 +5995,7 @@ public final ConnectableObservable replay(Scheduler scheduler) { *

* Any and all items emitted by the source Observable will be emitted by the resulting * Observable, even those emitted during failed subscriptions. For example, if an Observable - * fails at first but emits {@code [1, 2]} then succeeds the second time and emits - * {@code [1, 2, 3, 4, 5]} then the complete sequence of emissions and notifications would be + * fails at first but emits {@code [1, 2]} then succeeds the second time and emits {@code [1, 2, 3, 4, 5]} then the complete sequence of emissions and notifications would be * {@code [1, 2, 1, 2, 3, 4, 5, onCompleted]}. * * @return the source Observable modified with retry logic @@ -6081,8 +6006,7 @@ public final Observable retry() { } /** - * Return an Observable that mirrors the source Observable, resubscribing to it if it calls - * {@code onError} up to a certain number of retries. + * Return an Observable that mirrors the source Observable, resubscribing to it if it calls {@code onError} up to a certain number of retries. *

* *

@@ -6091,8 +6015,7 @@ public final Observable retry() { *

* Any and all items emitted by the source Observable will be emitted by the resulting * Observable, even those emitted during failed subscriptions. For example, if an Observable - * fails at first but emits {@code [1, 2]} then succeeds the second time and emits - * {@code [1, 2, 3, 4, 5]} then the complete sequence of emissions and notifications would be + * fails at first but emits {@code [1, 2]} then succeeds the second time and emits {@code [1, 2, 3, 4, 5]} then the complete sequence of emissions and notifications would be * {@code [1, 2, 1, 2, 3, 4, 5, onCompleted]}. * * @param retryCount @@ -6170,8 +6093,7 @@ public final Observable sample(Observable sampler) { * * @param accumulator * an accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be emitted to {@link Observer}s via - * {@link Observer#onNext onNext} and used in the next accumulator call + * Observable, whose result will be emitted to {@link Observer}s via {@link Observer#onNext onNext} and used in the next accumulator call * @return an Observable that emits the results of each call to the accumulator function * @see RxJava Wiki: scan() * @see MSDN: Observable.Scan @@ -6198,8 +6120,7 @@ public final Observable scan(Func2 accumulator) { * the initial (seed) accumulator item * @param accumulator * an accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be emitted to {@link Observer}s via - * {@link Observer#onNext onNext} and used in the next accumulator call + * Observable, whose result will be emitted to {@link Observer}s via {@link Observer#onNext onNext} and used in the next accumulator call * @return an Observable that emits the results of each call to the accumulator function * @see RxJava Wiki: scan() * @see MSDN: Observable.Scan @@ -6249,9 +6170,7 @@ public final Observable single(Func1 predicate) { /** * If the source Observable completes after emitting a single item, return an Observable that * emits that item; if the source Observable is empty, return an Observable that emits a default - * item. If the source Observable emits more than one item, throw an - * {@code IllegalArgumentException.} - *

+ * item. If the source Observable emits more than one item, throw an {@code IllegalArgumentException.}

* * * @param defaultValue @@ -6321,7 +6240,7 @@ public final Observable skip(int num) { * @see RxJava Wiki: skip() */ public final Observable skip(long time, TimeUnit unit) { - return skip(time, unit, Schedulers.threadPoolForComputation()); + return skip(time, unit, Schedulers.computation()); } /** @@ -6383,7 +6302,7 @@ public final Observable skipLast(int count) { * @see MSDN: Observable.SkipLast */ public final Observable skipLast(long time, TimeUnit unit) { - return skipLast(time, unit, Schedulers.threadPoolForComputation()); + return skipLast(time, unit, Schedulers.computation()); } /** @@ -6480,8 +6399,7 @@ public final Observable startWith(Iterable values) { } /** - * Returns an Observable that emits the items in a specified {@link Iterable}, on a specified - * {@link Scheduler} before it begins to emit items emitted by the source Observable. + * Returns an Observable that emits the items in a specified {@link Iterable}, on a specified {@link Scheduler} before it begins to emit items emitted by the source Observable. *

* * @@ -6733,6 +6651,11 @@ public final Observable startWith(T[] values, Scheduler scheduler) { return startWith(Arrays.asList(values), scheduler); } + // TODO should this be called `observe` instead of `subscribe`? + public final void subscribe(Operator o) { + f.call(o); + } + /** * Subscribe and ignore all events. * @@ -6764,7 +6687,8 @@ public final void onNext(T args) { * An {@link Observer} must call an Observable's {@code subscribe} method in order to receive * items and notifications from the Observable. * - * @param onNext FIXME FIXME FIXME + * @param onNext + * FIXME FIXME FIXME * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving * items before the Observable has finished sending them * @see RxJava Wiki: onNext, onCompleted, and onError @@ -6803,8 +6727,10 @@ public final void onNext(T args) { * An {@link Observer} must call an Observable's {@code subscribe} method in order to receive * items and notifications from the Observable. * - * @param onNext FIXME FIXME FIXME - * @param onError FIXME FIXME FIXME + * @param onNext + * FIXME FIXME FIXME + * @param onError + * FIXME FIXME FIXME * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving * items before the Observable has finished sending them * @see RxJava Wiki: onNext, onCompleted, and onError @@ -6847,9 +6773,12 @@ public final void onNext(T args) { * An {@link Observer} must call an Observable's {@code subscribe} method in order to receive * items and notifications from the Observable. * - * @param onNext FIXME FIXME FIXME - * @param onError FIXME FIXME FIXME - * @param onComplete FIXME FIXME FIXME + * @param onNext + * FIXME FIXME FIXME + * @param onError + * FIXME FIXME FIXME + * @param onComplete + * FIXME FIXME FIXME * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving * items before the Observable has finished sending them * @see RxJava Wiki: onNext, onCompleted, and onError @@ -6894,10 +6823,14 @@ public final void onNext(T args) { * An {@link Observer} must call an Observable's {@code subscribe} method in order to receive * items and notifications from the Observable. * - * @param onNext FIXME FIXME FIXME - * @param onError FIXME FIXME FIXME - * @param onComplete FIXME FIXME FIXME - * @param scheduler FIXME FIXME FIXME + * @param onNext + * FIXME FIXME FIXME + * @param onError + * FIXME FIXME FIXME + * @param onComplete + * FIXME FIXME FIXME + * @param scheduler + * FIXME FIXME FIXME * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving * items before the Observable has finished sending them * @see RxJava Wiki: onNext, onCompleted, and onError @@ -6910,9 +6843,12 @@ public final Subscription subscribe(final Action1 onNext, final Actio * An {@link Observer} must call an Observable's {@code subscribe} method in order to receive * items and notifications from the Observable. * - * @param onNext FIXME FIXME FIXME - * @param onError FIXME FIXME FIXME - * @param scheduler FIXME FIXME FIXME + * @param onNext + * FIXME FIXME FIXME + * @param onError + * FIXME FIXME FIXME + * @param scheduler + * FIXME FIXME FIXME * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving * items before the Observable has finished sending them * @see RxJava Wiki: onNext, onCompleted, and onError @@ -6925,8 +6861,10 @@ public final Subscription subscribe(final Action1 onNext, final Actio * An {@link Observer} must call an Observable's {@code subscribe} method in order to receive * items and notifications from the Observable. * - * @param onNext FIXME FIXME FIXME - * @param scheduler FIXME FIXME FIXME + * @param onNext + * FIXME FIXME FIXME + * @param scheduler + * FIXME FIXME FIXME * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving * items before the Observable has finished sending them * @see RxJava Wiki: onNext, onCompleted, and onError @@ -6941,16 +6879,13 @@ public final Subscription subscribe(final Action1 onNext, Scheduler s *

* A typical implementation of {@code subscribe} does the following: *

    - *
  1. It stores a reference to the Observer in a collection object, such as a {@code List} - * object.
  2. + *
  3. It stores a reference to the Observer in a collection object, such as a {@code List} object.
  4. *
  5. It returns a reference to the {@link Subscription} interface. This enables Observers to * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} - * method.
  6. + * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} method. *

* An {@code Observable} instance is responsible for accepting all subscriptions and - * notifying all Observers. Unless the documentation for a particular {@code Observable} - * implementation indicates otherwise, Observers should make no assumptions about the order in + * notifying all Observers. Unless the documentation for a particular {@code Observable} implementation indicates otherwise, Observers should make no assumptions about the order in * which multiple Observers will receive their notifications. *

* For more information see the @@ -6961,12 +6896,11 @@ public final Subscription subscribe(final Action1 onNext, Scheduler s * @return a {@link Subscription} reference with which the {@link Observer} can stop receiving * items before the Observable has finished sending them * @throws IllegalArgumentException - * if the {@link Observer} provided as the argument to {@code subscribe()} is - * {@code null} + * if the {@link Observer} provided as the argument to {@code subscribe()} is {@code null} */ public final Subscription subscribe(Observer observer) { // allow the hook to intercept and/or decorate - OnSubscribeFunc onSubscribeFunction = hook.onSubscribeStart(this, onSubscribe); + Action1> onSubscribeFunction = hook.onSubscribeStart(this, f); // validate and proceed if (observer == null) { throw new IllegalArgumentException("observer can not be null"); @@ -6976,23 +6910,20 @@ public final Subscription subscribe(Observer observer) { // the subscribe function can also be overridden but generally that's not the appropriate approach so I won't mention that in the exception } try { + Operator op = null; /** * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" */ if (isInternalImplementation(observer)) { - Subscription s = onSubscribeFunction.onSubscribe(observer); - if (s == null) { - // this generally shouldn't be the case on a 'trusted' onSubscribe but in case it happens - // we want to gracefully handle it the same as AtomicObservableSubscription does - return hook.onSubscribeReturn(this, Subscriptions.empty()); - } else { - return hook.onSubscribeReturn(this, s); - } + op = Operator.create(observer, new CompositeSubscription()); + onSubscribeFunction.call(op); } else { - SafeObservableSubscription subscription = new SafeObservableSubscription(); - subscription.wrap(onSubscribeFunction.onSubscribe(new SafeObserver(subscription, observer))); - return hook.onSubscribeReturn(this, subscription); + // TODO this doesn't seem correct any longer with the Operator and injecting of CompositeSubscription + SafeObservableSubscription subscription = new SafeObservableSubscription(op); + op = Operator.create(new SafeObserver(subscription, observer), new CompositeSubscription()); + onSubscribeFunction.call(op); } + return hook.onSubscribeReturn(this, op); } catch (OnErrorNotImplementedException e) { // special handling when onError is not implemented ... we just rethrow throw e; @@ -7020,16 +6951,13 @@ public final Subscription subscribe(Observer observer) { *

* A typical implementation of {@code subscribe} does the following: *

    - *
  1. It stores a reference to the Observer in a collection object, such as a {@code List} - * object.
  2. + *
  3. It stores a reference to the Observer in a collection object, such as a {@code List} object.
  4. *
  5. It returns a reference to the {@link Subscription} interface. This enables Observers to * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} - * method.
  6. + * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} method. *

* An {@code Observable} instance is responsible for accepting all subscriptions and - * notifying all Observers. Unless the documentation for a particular {@code Observable} - * implementation indicates otherwise, Observers should make no assumptions about the order in + * notifying all Observers. Unless the documentation for a particular {@code Observable} implementation indicates otherwise, Observers should make no assumptions about the order in * which multiple Observers will receive their notifications. *

* For more information see the @@ -7049,8 +6977,7 @@ public final Subscription subscribe(Observer observer, Scheduler sche } /** - * Asynchronously subscribes and unsubscribes Observers to this Observable on the specified - * {@link Scheduler}. + * Asynchronously subscribes and unsubscribes Observers to this Observable on the specified {@link Scheduler}. *

* * @@ -7142,8 +7069,7 @@ public final Observable sumLong(Func1 valueExtractor) { * @param func * a function that, when applied to an item emitted by the source Observable, returns * an Observable - * @return an Observable that emits the items emitted by the Observable returned from applying - * {@code func} to the most recently emitted item emitted by the source Observable + * @return an Observable that emits the items emitted by the Observable returned from applying {@code func} to the most recently emitted item emitted by the source Observable */ public final Observable switchMap(Func1> func) { return switchOnNext(map(func)); @@ -7155,13 +7081,10 @@ public final Observable switchMap(Func1 * *

- * A well-behaved Observable does not interleave its invocations of the - * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and - * {@link Observer#onError onError} methods of its {@link Observer}s; it invokes either - * {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after + * A well-behaved Observable does not interleave its invocations of the {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and {@link Observer#onError onError} methods of + * its {@link Observer}s; it invokes either {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after * invoking either {@code onCompleted} or {@code onError}. {@code synchronize} enforces this, - * and the Observable it returns invokes {@code onNext} and {@code onCompleted} or - * {@code onError} synchronously. + * and the Observable it returns invokes {@code onNext} and {@code onCompleted} or {@code onError} synchronously. * * @return an Observable that is a chronologically well-behaved version of the source * Observable, and that synchronously notifies its {@link Observer}s @@ -7178,13 +7101,10 @@ public final Observable synchronize() { *

* *

- * A well-behaved Observable does not interleave its invocations of the - * {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and - * {@link Observer#onError onError} methods of its {@link Observer}s; it invokes either - * {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after + * A well-behaved Observable does not interleave its invocations of the {@link Observer#onNext onNext}, {@link Observer#onCompleted onCompleted}, and {@link Observer#onError onError} methods of + * its {@link Observer}s; it invokes either {@code onCompleted} or {@code onError} only once; and it never invokes {@code onNext} after * invoking either {@code onCompleted} or {@code onError}. {@code synchronize} enforces this, - * and the Observable it returns invokes {@code onNext} and {@code onCompleted} or - * {@code onError} synchronously. + * and the Observable it returns invokes {@code onNext} and {@code onCompleted} or {@code onError} synchronously. * * @param lock * the lock object to synchronize each observer call on @@ -7202,8 +7122,7 @@ public final Observable synchronize(Object lock) { *

* *

- * This method returns an Observable that will invoke a subscribing {@link Observer}'s - * {@link Observer#onNext onNext} function a maximum of {@code num} times before invoking + * This method returns an Observable that will invoke a subscribing {@link Observer}'s {@link Observer#onNext onNext} function a maximum of {@code num} times before invoking * {@link Observer#onCompleted onCompleted}. * * @param num @@ -7214,7 +7133,7 @@ public final Observable synchronize(Object lock) { * @see RxJava Wiki: take() */ public final Observable take(final int num) { - return create(OperationTake.take(this, num)); + return bind(new OperatorTake(num)); } /** @@ -7232,7 +7151,7 @@ public final Observable take(final int num) { * @see RxJava Wiki: take() */ public final Observable take(long time, TimeUnit unit) { - return take(time, unit, Schedulers.threadPoolForComputation()); + return take(time, unit, Schedulers.computation()); } /** @@ -7252,7 +7171,7 @@ public final Observable take(long time, TimeUnit unit) { * @see RxJava Wiki: take() */ public final Observable take(long time, TimeUnit unit, Scheduler scheduler) { - return create(new OperationTake.TakeTimed(this, time, unit, scheduler)); + return create(new OperatorTakeTimed.TakeTimed(this, time, unit, scheduler)); } /** @@ -7323,7 +7242,7 @@ public final Observable takeLast(final int count) { * were emitted in a specified window of time before the Observable completed. */ public final Observable takeLast(int count, long time, TimeUnit unit) { - return takeLast(count, time, unit, Schedulers.threadPoolForComputation()); + return takeLast(count, time, unit, Schedulers.computation()); } /** @@ -7366,7 +7285,7 @@ public final Observable takeLast(int count, long time, TimeUnit unit, Schedul * the window of time before the Observable completed specified by {@code time} */ public final Observable takeLast(long time, TimeUnit unit) { - return takeLast(time, unit, Schedulers.threadPoolForComputation()); + return takeLast(time, unit, Schedulers.computation()); } /** @@ -7556,7 +7475,7 @@ public final Observable takeWhileWithIndex(final Func2RxJava Wiki: then() * @see MSDN: Observable.Then */ @@ -7568,8 +7487,7 @@ public final Plan0 then(Func1 selector) { * Returns an Observable that emits only the first item emitted by the source Observable during * sequential time windows of a specified duration. *

- * This differs from {@link #throttleLast} in that this only tracks passage of time whereas - * {@link #throttleLast} ticks at scheduled intervals. + * This differs from {@link #throttleLast} in that this only tracks passage of time whereas {@link #throttleLast} ticks at scheduled intervals. *

* * @@ -7589,8 +7507,7 @@ public final Observable throttleFirst(long windowDuration, TimeUnit unit) { * sequential time windows of a specified duration, where the windows are managed by a specified * Scheduler. *

- * This differs from {@link #throttleLast} in that this only tracks passage of time whereas - * {@link #throttleLast} ticks at scheduled intervals. + * This differs from {@link #throttleLast} in that this only tracks passage of time whereas {@link #throttleLast} ticks at scheduled intervals. *

* * @@ -7800,8 +7717,7 @@ public final Observable timeout(Func0> firstTi * item must arrive in order to continue the sequence * @param other * the fallback Observable to switch to if the source Observable times out - * @return an Observable that mirrors the source Observable, but switches to the {@code other} - * Observable if either the first item emitted by the source Observable or any + * @return an Observable that mirrors the source Observable, but switches to the {@code other} Observable if either the first item emitted by the source Observable or any * subsequent item don't arrive within time windows defined by the timeout selectors */ public final Observable timeout(Func0> firstTimeoutSelector, Func1> timeoutSelector, Observable other) { @@ -7958,8 +7874,7 @@ public final Observable timeout(long timeout, TimeUnit timeUnit, Scheduler sc } /** - * Returns an Observable that emits each item emitted by the source Observable, wrapped in a - * {@link Timestamped} object. + * Returns an Observable that emits each item emitted by the source Observable, wrapped in a {@link Timestamped} object. *

* * @@ -7968,12 +7883,11 @@ public final Observable timeout(long timeout, TimeUnit timeUnit, Scheduler sc * @see MSDN: Observable.Timestamp */ public final Observable> timestamp() { - return create(OperationTimestamp.timestamp(this)); + return timestamp(Schedulers.immediate()); } /** - * Returns an Observable that emits each item emitted by the source Observable, wrapped in a - * {@link Timestamped} object whose timestamps are provided by a specified Scheduler. + * Returns an Observable that emits each item emitted by the source Observable, wrapped in a {@link Timestamped} object whose timestamps are provided by a specified Scheduler. *

* * @@ -7985,7 +7899,7 @@ public final Observable> timestamp() { * @see MSDN: Observable.Timestamp */ public final Observable> timestamp(Scheduler scheduler) { - return create(OperationTimestamp.timestamp(this, scheduler)); + return bind(new OperatorTimestamp(scheduler)); } /** @@ -8005,8 +7919,7 @@ public final BlockingObservable toBlockingObservable() { *

* *

- * Normally, an Observable that returns multiple items will do so by invoking its - * {@link Observer}'s {@link Observer#onNext onNext} method for each such item. You can change + * Normally, an Observable that returns multiple items will do so by invoking its {@link Observer}'s {@link Observer#onNext onNext} method for each such item. You can change * this behavior, instructing the Observable to compose a list of all of these items and then to * invoke the Observer's {@code onNext} function once, passing it the entire list, by calling * the Observable's {@code toList} method prior to calling its {@link #subscribe} method. @@ -8019,7 +7932,7 @@ public final BlockingObservable toBlockingObservable() { * @see RxJava Wiki: toList() */ public final Observable> toList() { - return create(OperationToObservableList.toObservableList(this)); + return bind(new OperatorToObservableList()); } /** @@ -8044,8 +7957,7 @@ public final Observable> toMap(Func1 keySe /** * Return an Observable that emits a single HashMap containing values corresponding to items - * emitted by the source Observable, mapped by the keys returned by a specified - * {@code keySelector} function. + * emitted by the source Observable, mapped by the keys returned by a specified {@code keySelector} function. *

* *

@@ -8066,8 +7978,7 @@ public final Observable> toMap(Func1 ke } /** - * Return an Observable that emits a single Map, returned by a specified {@code mapFactory} - * function, that contains keys and values extracted from the items emitted by the source + * Return an Observable that emits a single Map, returned by a specified {@code mapFactory} function, that contains keys and values extracted from the items emitted by the source * Observable. *

* @@ -8128,10 +8039,8 @@ public final Observable>> toMultimap(Func1 * * @@ -8152,10 +8061,8 @@ public final Observable>> toMultimap(Func1 * * @@ -8180,8 +8087,7 @@ public final Observable>> toMultimap(Func1 * * @@ -8193,7 +8099,7 @@ public final Observable>> toMultimap(Func1RxJava Wiki: toSortedList() */ public final Observable> toSortedList() { - return create(OperationToObservableSortedList.toSortedList(this)); + return bind(new OperatorToObservableSortedList()); } /** @@ -8210,7 +8116,7 @@ public final Observable> toSortedList() { * @see RxJava Wiki: toSortedList() */ public final Observable> toSortedList(Func2 sortFunction) { - return create(OperationToObservableSortedList.toSortedList(this, sortFunction)); + return bind(new OperatorToObservableSortedList(sortFunction)); } /** @@ -8219,8 +8125,7 @@ public final Observable> toSortedList(Func2 * * @param predicate - * a function that evaluates an item emitted by the source Observable, returning - * {@code true} if it passes the filter + * a function that evaluates an item emitted by the source Observable, returning {@code true} if it passes the filter * @return an Observable that emits only those items emitted by the source Observable that the * filter evaluates as {@code true} * @see RxJava Wiki: where() @@ -8234,8 +8139,7 @@ public final Observable where(Func1 predicate) { /** * Returns an Observable that emits windows of items it collects from the source Observable. * The resulting Observable emits connected, non-overlapping windows. It emits the current - * window and opens a new one when the Observable produced by the specified - * {@code closingSelector} emits an item. The {@code closingSelector} then creates a new + * window and opens a new one when the Observable produced by the specified {@code closingSelector} emits an item. The {@code closingSelector} then creates a new * Observable to generate the closer of the next window. *

* @@ -8254,8 +8158,7 @@ public final Observable> window(Func0 @@ -8263,8 +8166,7 @@ public final Observable> window(Func0RxJava Wiki: window() */ public final Observable> window(int count) { @@ -8283,11 +8185,8 @@ public final Observable> window(int count) { * @param count * the maximum size of each window before it should be emitted * @param skip - * how many items need to be skipped before starting a new window. Note that if - * {@code skip} and {@code count} are equal this is the same operation as - * {@link #window(int)}. - * @return an Observable that emits windows every {@code skip} items containing at most - * {@code count} items from the source Observable + * how many items need to be skipped before starting a new window. Note that if {@code skip} and {@code count} are equal this is the same operation as {@link #window(int)}. + * @return an Observable that emits windows every {@code skip} items containing at most {@code count} items from the source Observable * @see RxJava Wiki: window() */ public final Observable> window(int count, int skip) { @@ -8296,9 +8195,8 @@ public final Observable> window(int count, int skip) { /** * Returns an Observable that emits windows of items it collects from the source Observable. - * The resulting Observable starts a new window periodically, as determined by the - * {@code timeshift} argument. It emits each window after a fixed timespan, specified by the - * {@code timespan} argument. When the source Observable completes or Observable completes or + * The resulting Observable starts a new window periodically, as determined by the {@code timeshift} argument. It emits each window after a fixed timespan, specified by the {@code timespan} + * argument. When the source Observable completes or Observable completes or * encounters an error, the resulting Observable emits the current window and propagates the * notification from the source Observable. *

@@ -8309,8 +8207,7 @@ public final Observable> window(int count, int skip) { * @param timeshift * the period of time after which a new window will be created * @param unit - * the unit of time that applies to the {@code timespan} and {@code timeshift} - * arguments + * the unit of time that applies to the {@code timespan} and {@code timeshift} arguments * @return an Observable that emits new windows periodically as a fixed timespan elapses * @see RxJava Wiki: window() */ @@ -8320,9 +8217,8 @@ public final Observable> window(long timespan, long timeshift, Tim /** * Returns an Observable that emits windows of items it collects from the source Observable. - * The resulting Observable starts a new window periodically, as determined by the - * {@code timeshift} argument. It emits each window after a fixed timespan, specified by the - * {@code timespan} argument. When the source Observable completes or Observable completes or + * The resulting Observable starts a new window periodically, as determined by the {@code timeshift} argument. It emits each window after a fixed timespan, specified by the {@code timespan} + * argument. When the source Observable completes or Observable completes or * encounters an error, the resulting Observable emits the current window and propagates the * notification from the source Observable. *

@@ -8333,8 +8229,7 @@ public final Observable> window(long timespan, long timeshift, Tim * @param timeshift * the period of time after which a new window will be created * @param unit - * the unit of time that applies to the {@code timespan} and {@code timeshift} - * arguments + * the unit of time that applies to the {@code timespan} and {@code timeshift} arguments * @param scheduler * the {@link Scheduler} to use when determining the end and start of a window * @return an Observable that emits new windows periodically as a fixed timespan elapses @@ -8369,8 +8264,7 @@ public final Observable> window(long timespan, TimeUnit unit) { /** * Returns an Observable that emits windows of items it collects from the source Observable. * The resulting Observable emits connected, non-overlapping windows, each of a fixed duration - * as specified by the {@code timespan} argument or a maximum size as specified by the - * {@code count} argument (whichever is reached first). When the source Observable completes or + * as specified by the {@code timespan} argument or a maximum size as specified by the {@code count} argument (whichever is reached first). When the source Observable completes or * encounters an error, the resulting Observable emits the current window and propagates the * notification from the source Observable. *

@@ -8395,8 +8289,7 @@ public final Observable> window(long timespan, TimeUnit unit, int /** * Returns an Observable that emits windows of items it collects from the source Observable. The * resulting Observable emits connected, non-overlapping windows, each of a fixed duration - * specified by the {@code timespan} argument or a maximum size specified by the {@code count} - * argument (whichever is reached first). When the source Observable completes or encounters an + * specified by the {@code timespan} argument or a maximum size specified by the {@code count} argument (whichever is reached first). When the source Observable completes or encounters an * error, the resulting Observable emits the current window and propagates the notification from * the source Observable. *

@@ -8502,8 +8395,7 @@ public final Observable> window(Observable boundary) { * @param zipFunction * a function that combines the pairs of items from the Observable and the Iterable * to generate the items to be emitted by the resulting Observable - * @return an Observable that pairs up values from the source Observable and the {@code other} - * Iterable sequence and emits the results of {@code zipFunction} applied to these pairs + * @return an Observable that pairs up values from the source Observable and the {@code other} Iterable sequence and emits the results of {@code zipFunction} applied to these pairs */ public final Observable zip(Iterable other, Func2 zipFunction) { return create(OperationZip.zipIterable(this, other, zipFunction)); @@ -8524,8 +8416,7 @@ public final Observable zip(Iterable other, Func2 Observable zip(Observable other, Func2 zipFunction) { return zip(this, other, zipFunction); @@ -8541,11 +8432,11 @@ public final Observable zip(Observable other, Func2 extends Observable { public NeverObservable() { - super(new OnSubscribeFunc() { + super(new Action1>() { @Override - public final Subscription onSubscribe(Observer t1) { - return Subscriptions.empty(); + public void call(Operator observer) { + // do nothing } }); @@ -8553,8 +8444,7 @@ public final Subscription onSubscribe(Observer t1) { } /** - * An Observable that invokes {@link Observer#onError onError} when the {@link Observer} - * subscribes to it. + * An Observable that invokes {@link Observer#onError onError} when the {@link Observer} subscribes to it. * * @param * the type of item (ostensibly) emitted by the Observable @@ -8562,7 +8452,7 @@ public final Subscription onSubscribe(Observer t1) { private static class ThrowObservable extends Observable { public ThrowObservable(final Throwable exception) { - super(new OnSubscribeFunc() { + super(new Action1>() { /** * Accepts an {@link Observer} and calls its {@link Observer#onError onError} method. @@ -8572,16 +8462,15 @@ public ThrowObservable(final Throwable exception) { * @return a reference to the subscription */ @Override - public final Subscription onSubscribe(Observer observer) { + public void call(Operator observer) { observer.onError(exception); - return Subscriptions.empty(); } }); } - } + @SuppressWarnings("rawtypes") private final static ConcurrentHashMap internalClassMap = new ConcurrentHashMap(); /** @@ -8593,9 +8482,9 @@ public final Subscription onSubscribe(Observer observer) { * Note: If strong reasons for not depending on package names comes up then the implementation * of this method can change to looking for a marker interface. * - * @param o FIXME FIXME FIXME - * @return {@code true} if the given function is an internal implementation, and {@code false} - * otherwise + * @param o + * FIXME FIXME FIXME + * @return {@code true} if the given function is an internal implementation, and {@code false} otherwise */ private boolean isInternalImplementation(Object o) { if (o == null) { diff --git a/rxjava-core/src/main/java/rx/Operator.java b/rxjava-core/src/main/java/rx/Operator.java new file mode 100644 index 0000000000..3df3485006 --- /dev/null +++ b/rxjava-core/src/main/java/rx/Operator.java @@ -0,0 +1,70 @@ +package rx; + +import rx.subscriptions.CompositeSubscription; + +public abstract class Operator implements Observer, Subscription { + + private final CompositeSubscription cs; + + // TODO I'm questioning this API, it could be confusing and misused + protected Operator(Operator op) { + this.cs = op.cs; + } + + protected Operator(CompositeSubscription cs) { + this.cs = cs; + } + + public static Operator create(final Observer o, CompositeSubscription cs) { + if (o == null) { + throw new IllegalArgumentException("Observer can not be null"); + } + if (cs == null) { + throw new IllegalArgumentException("CompositeSubscription can not be null"); + } + return new Operator(cs) { + + @Override + public void onCompleted() { + o.onCompleted(); + } + + @Override + public void onError(Throwable e) { + o.onError(e); + } + + @Override + public void onNext(T v) { + o.onNext(v); + } + + }; + } + + public static Operator create(final Observer o, Subscription s) { + if (s == null) { + throw new IllegalArgumentException("Subscription can not be null"); + } + CompositeSubscription cs = new CompositeSubscription(); + cs.add(s); + + return create(o, cs); + } + + /** + * Used to register an unsubscribe callback. + */ + public final void add(Subscription s) { + cs.add(s); + } + + @Override + public final void unsubscribe() { + cs.unsubscribe(); + } + + public final boolean isUnsubscribed() { + return cs.isUnsubscribed(); + } +} \ No newline at end of file diff --git a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java index ec117da2ba..37ae4c662d 100644 --- a/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java +++ b/rxjava-core/src/main/java/rx/observables/ConnectableObservable.java @@ -17,8 +17,10 @@ import rx.Observable; import rx.Observer; +import rx.Operator; import rx.Subscription; import rx.operators.OperationRefCount; +import rx.util.functions.Action1; /** * A ConnectableObservable resembles an ordinary {@link Observable}, except that it does not begin @@ -37,7 +39,7 @@ public abstract class ConnectableObservable extends Observable { - protected ConnectableObservable(OnSubscribeFunc onSubscribe) { + protected ConnectableObservable(Action1> onSubscribe) { super(onSubscribe); } diff --git a/rxjava-core/src/main/java/rx/observables/GroupedObservable.java b/rxjava-core/src/main/java/rx/observables/GroupedObservable.java index a43a02c010..fdd7952c8c 100644 --- a/rxjava-core/src/main/java/rx/observables/GroupedObservable.java +++ b/rxjava-core/src/main/java/rx/observables/GroupedObservable.java @@ -16,6 +16,8 @@ package rx.observables; import rx.Observable; +import rx.Operator; +import rx.util.functions.Action1; import rx.util.functions.Func1; /** @@ -31,7 +33,7 @@ public class GroupedObservable extends Observable { private final K key; - public GroupedObservable(K key, OnSubscribeFunc onSubscribe) { + public GroupedObservable(K key, Action1> onSubscribe) { super(onSubscribe); this.key = key; } diff --git a/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java b/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java deleted file mode 100644 index ebe2392bf8..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperationGroupBy.java +++ /dev/null @@ -1,241 +0,0 @@ -/** - * Copyright 2014 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.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; -import rx.observables.GroupedObservable; -import rx.util.functions.Func1; -import rx.util.functions.Functions; - -/** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as Observables, one Observable per group. - *

- * - */ -public final class OperationGroupBy { - - public static OnSubscribeFunc> groupBy(Observable source, final Func1 keySelector, final Func1 elementSelector) { - - final Observable> keyval = source.map(new Func1>() { - @Override - public KeyValue call(T t) { - K key = keySelector.call(t); - R value = elementSelector.call(t); - - return new KeyValue(key, value); - } - }); - - return new GroupBy(keyval); - } - - public static OnSubscribeFunc> groupBy(Observable source, final Func1 keySelector) { - return groupBy(source, keySelector, Functions. identity()); - } - - private static class GroupBy implements OnSubscribeFunc> { - - private final Observable> source; - private final ConcurrentHashMap> groupedObservables = new ConcurrentHashMap>(); - private final SafeObservableSubscription actualParentSubscription = new SafeObservableSubscription(); - private final AtomicInteger numGroupSubscriptions = new AtomicInteger(); - private final AtomicBoolean unsubscribeRequested = new AtomicBoolean(false); - - private GroupBy(Observable> source) { - this.source = source; - } - - @Override - public Subscription onSubscribe(final Observer> observer) { - final GroupBy _this = this; - actualParentSubscription.wrap(source.subscribe(new Observer>() { - - @Override - public void onCompleted() { - // we need to propagate to all children I imagine ... we can't just leave all of those Observable/Observers hanging - for (GroupedSubject o : groupedObservables.values()) { - o.onCompleted(); - } - // now the parent - observer.onCompleted(); - } - - @Override - public void onError(Throwable e) { - // we need to propagate to all children I imagine ... we can't just leave all of those Observable/Observers hanging - for (GroupedSubject o : groupedObservables.values()) { - o.onError(e); - } - // now the parent - observer.onError(e); - } - - @Override - public void onNext(KeyValue value) { - GroupedSubject gs = groupedObservables.get(value.key); - if (gs == null) { - if (unsubscribeRequested.get()) { - // unsubscribe has been requested so don't create new groups - // only send data to groups already created - return; - } - /* - * Technically the source should be single-threaded so we shouldn't need to do this but I am - * programming defensively as most operators are so this can work with a concurrent sequence - * if it ends up receiving one. - */ - GroupedSubject newGs = GroupedSubject. create(value.key, _this); - GroupedSubject existing = groupedObservables.putIfAbsent(value.key, newGs); - if (existing == null) { - // we won so use the one we created - gs = newGs; - // since we won the creation we emit this new GroupedObservable - observer.onNext(gs); - } else { - // another thread beat us so use the existing one - gs = existing; - } - } - gs.onNext(value.value); - } - })); - - return new Subscription() { - - @Override - public void unsubscribe() { - if (numGroupSubscriptions.get() == 0) { - // if we have no group subscriptions we will unsubscribe - actualParentSubscription.unsubscribe(); - // otherwise we mark to not send any more groups (waiting on existing groups to finish) - unsubscribeRequested.set(true); - } - } - }; - } - - /** - * Children notify of being subscribed to. - * - * @param key - */ - private void subscribeKey(K key) { - numGroupSubscriptions.incrementAndGet(); - } - - /** - * Children notify of being unsubscribed from. - * - * @param key - */ - private void unsubscribeKey(K key) { - int c = numGroupSubscriptions.decrementAndGet(); - if (c == 0) { - actualParentSubscription.unsubscribe(); - } - } - } - - private static class GroupedSubject extends GroupedObservable implements Observer { - - static GroupedSubject create(final K key, final GroupBy parent) { - final AtomicReference> subscribedObserver = new AtomicReference>(OperationGroupBy. emptyObserver()); - return new GroupedSubject(key, new OnSubscribeFunc() { - - private final SafeObservableSubscription subscription = new SafeObservableSubscription(); - - @Override - public Subscription onSubscribe(Observer observer) { - // register Observer - subscribedObserver.set(observer); - - parent.subscribeKey(key); - - return subscription.wrap(new Subscription() { - @Override - public void unsubscribe() { - // we remove the Observer so we stop emitting further events (they will be ignored if parent continues to send) - subscribedObserver.set(OperationGroupBy. emptyObserver()); - // now we need to notify the parent that we're unsubscribed - parent.unsubscribeKey(key); - } - }); - } - }, subscribedObserver); - } - - private final AtomicReference> subscribedObserver; - - public GroupedSubject(K key, OnSubscribeFunc onSubscribe, AtomicReference> subscribedObserver) { - super(key, onSubscribe); - this.subscribedObserver = subscribedObserver; - } - - @Override - public void onCompleted() { - subscribedObserver.get().onCompleted(); - } - - @Override - public void onError(Throwable e) { - subscribedObserver.get().onError(e); - } - - @Override - public void onNext(T v) { - subscribedObserver.get().onNext(v); - } - - } - - private static Observer emptyObserver() { - return new Observer() { - @Override - public void onCompleted() { - // do nothing - } - - @Override - public void onError(Throwable e) { - // do nothing - } - - @Override - public void onNext(T t) { - // do nothing - } - }; - } - - private static class KeyValue { - private final K key; - private final V value; - - private KeyValue(K key, V value) { - this.key = key; - this.value = value; - } - } -} diff --git a/rxjava-core/src/main/java/rx/operators/OperationGroupByUntil.java b/rxjava-core/src/main/java/rx/operators/OperationGroupByUntil.java index 5fcca3da4f..7e0af156fe 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationGroupByUntil.java +++ b/rxjava-core/src/main/java/rx/operators/OperationGroupByUntil.java @@ -23,13 +23,14 @@ import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; +import rx.Operator; import rx.Subscription; import rx.observables.GroupedObservable; import rx.subjects.PublishSubject; import rx.subjects.Subject; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.SerialSubscription; -import rx.subscriptions.Subscriptions; +import rx.util.functions.Action1; import rx.util.functions.Func1; /** @@ -58,7 +59,7 @@ public OperationGroupByUntil(Observable source, public Subscription onSubscribe(Observer> t1) { SerialSubscription cancel = new SerialSubscription(); ResultSink sink = new ResultSink(t1, cancel); - cancel.setSubscription(sink.run()); + cancel.set(sink.run()); return cancel; } @@ -82,7 +83,7 @@ public Subscription run() { SerialSubscription toSource = new SerialSubscription(); group.add(toSource); - toSource.setSubscription(source.subscribe(this)); + toSource.set(source.subscribe(this)); return group; } @@ -127,7 +128,7 @@ public void onNext(TSource args) { group.add(durationHandle); DurationObserver durationObserver = new DurationObserver(key, durationHandle); - durationHandle.setSubscription(duration.subscribe(durationObserver)); + durationHandle.set(duration.subscribe(durationObserver)); } @@ -212,10 +213,10 @@ public static class GroupSubject extends GroupedObservable implement protected final Subject publish; public GroupSubject(K key, final Subject publish) { - super(key, new OnSubscribeFunc() { + super(key, new Action1>() { @Override - public Subscription onSubscribe(Observer o) { - return publish.subscribe(o); + public void call(Operator o) { + publish.subscribe(o); } }); this.publish = publish; diff --git a/rxjava-core/src/main/java/rx/operators/OperationMap.java b/rxjava-core/src/main/java/rx/operators/OperationMap.java deleted file mode 100644 index e224c40eeb..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperationMap.java +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright 2014 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.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; -import rx.util.functions.Func1; -import rx.util.functions.Func2; - -/** - * Applies a function of your choosing to every item emitted by an Observable, and returns this - * transformation as a new Observable. - *

- * - */ -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 - * the type of the input sequence. - * @param - * 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 OnSubscribeFunc map(final Observable sequence, final Func1 func) { - return mapWithIndex(sequence, new Func2() { - @Override - public R call(T value, @SuppressWarnings("unused") Integer unused) { - return func.call(value); - } - }); - } - - /** - * 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. The function gets the index of the emitted item - * as additional parameter. - * @param - * the type of the input sequence. - * @param - * 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. - * @deprecated - */ - public static OnSubscribeFunc mapWithIndex(final Observable sequence, final Func2 func) { - return new OnSubscribeFunc() { - @Override - public Subscription onSubscribe(Observer observer) { - return new MapObservable(sequence, func).onSubscribe(observer); - } - }; - } - - /** - * An observable sequence that is the result of applying a transformation to each item in an input sequence. - * - * @param - * the type of the input sequence. - * @param - * the type of the output sequence. - */ - private static class MapObservable implements OnSubscribeFunc { - public MapObservable(Observable sequence, Func2 func) { - this.sequence = sequence; - this.func = func; - } - - private final Observable sequence; - private final Func2 func; - private int index; - - @Override - public Subscription onSubscribe(final Observer observer) { - final SafeObservableSubscription subscription = new SafeObservableSubscription(); - return subscription.wrap(sequence.subscribe(new SafeObserver(subscription, new Observer() { - @Override - public void onNext(T value) { - observer.onNext(func.call(value, index)); - index++; - } - - @Override - public void onError(Throwable ex) { - observer.onError(ex); - } - - @Override - public void onCompleted() { - observer.onCompleted(); - } - }))); - } - } -} diff --git a/rxjava-core/src/main/java/rx/operators/OperationMerge.java b/rxjava-core/src/main/java/rx/operators/OperationMerge.java deleted file mode 100644 index 3a69d64981..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperationMerge.java +++ /dev/null @@ -1,230 +0,0 @@ -/** - * Copyright 2014 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.LinkedList; - -import rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; -import rx.subscriptions.CompositeSubscription; - -/** - * Flattens a list of Observables into one Observable sequence, without any transformation. - *

- * - *

- * You can combine the items emitted by multiple Observables so that they act like a single - * Observable, by using the merge operation. - */ -public final class OperationMerge { - - /** - * Flattens the observable sequences from the list of Observables into one observable sequence without any transformation. - * - * @param o - * 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 Observable.Merge(TSource) Method (IObservable(TSource)[]) - */ - public static OnSubscribeFunc merge(final Observable> o) { - return merge(o, Integer.MAX_VALUE); - } - - public static OnSubscribeFunc merge(final Observable> o, final int maxConcurrent) { - if (maxConcurrent <= 0) { - throw new IllegalArgumentException("maxConcurrent must be positive"); - } - return new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(Observer observer) { - return new MergeObservable(o, maxConcurrent).onSubscribe(observer); - } - }; - } - - /** - * 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. - *

- * It IS thread-safe from within it while receiving onNext events from multiple threads. - *

- * 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. - *

- * Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow. - * - * @param - */ - private static final class MergeObservable implements OnSubscribeFunc { - private final Observable> sequences; - private final CompositeSubscription ourSubscription = new CompositeSubscription(); - private volatile boolean parentCompleted = false; - private final LinkedList> pendingObservables = new LinkedList>(); - private volatile int activeObservableCount = 0; - private final int maxConcurrent; - /** - * Protect both pendingObservables and activeObservableCount from concurrent accesses. - */ - private final Object gate = new Object(); - - private MergeObservable(Observable> sequences, int maxConcurrent) { - this.sequences = sequences; - this.maxConcurrent = maxConcurrent; - } - - public Subscription onSubscribe(Observer actualObserver) { - - /** - * We must synchronize a merge because we subscribe to multiple sequences in parallel that will each be emitting. - *

- * The calls from each sequence must be serialized. - *

- * Bug report: https://github.com/Netflix/RxJava/issues/200 - */ - SafeObservableSubscription subscription = new SafeObservableSubscription(ourSubscription); - SynchronizedObserver synchronizedObserver = new SynchronizedObserver( - new SafeObserver(subscription, actualObserver), // Create a SafeObserver as SynchronizedObserver does not automatically unsubscribe - subscription); - - /** - * Subscribe to the parent Observable to get to the children Observables - */ - ourSubscription.add(sequences.subscribe(new ParentObserver(synchronizedObserver))); - - return subscription; - } - - /** - * Subscribe to the top level Observable to receive the sequence of Observable children. - * - * @param - */ - private class ParentObserver implements Observer> { - private final SynchronizedObserver synchronizedObserver; - - public ParentObserver(SynchronizedObserver synchronizedObserver) { - this.synchronizedObserver = synchronizedObserver; - } - - @Override - public void onCompleted() { - parentCompleted = true; - if (ourSubscription.isUnsubscribed()) { - return; - } - // 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 (isStopped()) { - synchronizedObserver.onCompleted(); - } - } - - @Override - public void onError(Throwable e) { - synchronizedObserver.onError(e); - } - - @Override - public void onNext(Observable childObservable) { - if (ourSubscription.isUnsubscribed()) { - // we won't act on any further items - return; - } - - if (childObservable == null) { - throw new IllegalArgumentException("Observable can not be null."); - } - - Observable observable = null; - synchronized (gate) { - if (activeObservableCount >= maxConcurrent) { - pendingObservables.add(childObservable); - } - else { - observable = childObservable; - activeObservableCount++; - } - } - if (observable != null) { - ourSubscription.add(observable.subscribe(new ChildObserver( - synchronizedObserver))); - } - } - } - - /** - * Subscribe to each child Observable and forward their sequence of data to the actualObserver - * - */ - private class ChildObserver implements Observer { - - private final SynchronizedObserver synchronizedObserver; - - public ChildObserver(SynchronizedObserver synchronizedObserver) { - this.synchronizedObserver = synchronizedObserver; - } - - @Override - public void onCompleted() { - if (ourSubscription.isUnsubscribed()) { - return; - } - - Observable childObservable = null; - // Try to fetch a pending observable - synchronized (gate) { - childObservable = pendingObservables.poll(); - if (childObservable == null) { - // There is no pending observable, decrease activeObservableCount. - activeObservableCount--; - } - else { - // Fetch an observable successfully. - // We will subscribe(this) at once. So don't change activeObservableCount. - } - } - if (childObservable != null) { - ourSubscription.add(childObservable.subscribe(this)); - } else { - // No pending observable. Need to check if it's necessary to emit an onCompleted - if (isStopped()) { - synchronizedObserver.onCompleted(); - } - } - } - - @Override - public void onError(Throwable e) { - synchronizedObserver.onError(e); - } - - @Override - public void onNext(T args) { - synchronizedObserver.onNext(args); - } - - } - - private boolean isStopped() { - synchronized (gate) { - return parentCompleted && activeObservableCount == 0 - && pendingObservables.size() == 0; - } - } - } -} diff --git a/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java b/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java index 959675c298..ee5cd22860 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMergeDelayError.java @@ -28,7 +28,7 @@ import rx.util.CompositeException; /** - * This behaves like {@link OperationMerge} except that if any of the merged Observables notify of + * This behaves like {@link OperatorMerge} except that if any of the merged Observables notify of * an error via onError, mergeDelayError will refrain from propagating that error * notification until all of the merged Observables have finished emitting items. *

diff --git a/rxjava-core/src/main/java/rx/operators/OperationMulticast.java b/rxjava-core/src/main/java/rx/operators/OperationMulticast.java index bbffa40ac5..102ac69d71 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationMulticast.java +++ b/rxjava-core/src/main/java/rx/operators/OperationMulticast.java @@ -18,11 +18,13 @@ import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; +import rx.Operator; import rx.Subscription; import rx.observables.ConnectableObservable; import rx.subjects.Subject; import rx.subscriptions.CompositeSubscription; import rx.subscriptions.Subscriptions; +import rx.util.functions.Action1; import rx.util.functions.Func0; import rx.util.functions.Func1; @@ -40,10 +42,10 @@ private static class MulticastConnectableObservable extends ConnectableObs private Subscription subscription; public MulticastConnectableObservable(Observable source, final Subject subject) { - super(new OnSubscribeFunc() { + super(new Action1>() { @Override - public Subscription onSubscribe(Observer observer) { - return subject.subscribe(observer); + public void call(Operator observer) { + subject.subscribe(observer); } }); this.source = source; diff --git a/rxjava-core/src/main/java/rx/operators/OperationParallel.java b/rxjava-core/src/main/java/rx/operators/OperationParallel.java deleted file mode 100644 index 4cc2f1679b..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperationParallel.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2014 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.Scheduler; -import rx.observables.GroupedObservable; -import rx.schedulers.Schedulers; -import rx.util.functions.Func0; -import rx.util.functions.Func1; - -/** - * Identifies unit of work that can be executed in parallel on a given Scheduler. - */ -public final class OperationParallel { - - public static Observable parallel(Observable source, Func1, Observable> f) { - return parallel(source, f, Schedulers.threadPoolForComputation()); - } - - public static Observable parallel(final Observable source, final Func1, Observable> f, final Scheduler s) { - return Observable.defer(new Func0>() { - - @Override - public Observable call() { - final AtomicInteger i = new AtomicInteger(0); - return source.groupBy(new Func1() { - - @Override - public Integer call(T t) { - return i.incrementAndGet() % s.degreeOfParallelism(); - } - - }).mergeMap(new Func1, Observable>() { - - @Override - public Observable call(GroupedObservable group) { - return f.call(group.observeOn(s)); - } - }); - } - }); - } -} diff --git a/rxjava-core/src/main/java/rx/operators/OperationReplay.java b/rxjava-core/src/main/java/rx/operators/OperationReplay.java index e8574c98da..a381ff6e98 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationReplay.java +++ b/rxjava-core/src/main/java/rx/operators/OperationReplay.java @@ -28,12 +28,14 @@ import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; +import rx.Operator; import rx.Scheduler; import rx.Subscription; import rx.subjects.Subject; import rx.subscriptions.Subscriptions; import rx.util.Timestamped; import rx.util.functions.Action0; +import rx.util.functions.Action1; import rx.util.functions.Func1; import rx.util.functions.Functions; @@ -61,8 +63,16 @@ public static Subject replayBuffered(int bufferSize) { * propagated through the given wrapped subject. */ public static Subject createScheduledSubject(Subject subject, Scheduler scheduler) { - Observable observedOn = subject.observeOn(scheduler); - SubjectWrapper s = new SubjectWrapper(subscriberOf(observedOn), subject); + final Observable observedOn = subject.observeOn(scheduler); + SubjectWrapper s = new SubjectWrapper(new Action1>() { + + @Override + public void call(Operator o) { + // TODO HACK between OnSubscribeFunc and Action1 + subscriberOf(observedOn).onSubscribe(o); + } + + }, subject); return s; } @@ -154,7 +164,7 @@ public static final class MappingSubject extends Subject { private final Subject subject; private final Func1 selector; - public MappingSubject(OnSubscribeFunc func, Subject subject, Func1 selector) { + public MappingSubject(Action1> func, Subject subject, Func1 selector) { super(func); this.subject = subject; this.selector = selector; @@ -184,7 +194,7 @@ public static final class SubjectWrapper extends Subject { /** The wrapped subject. */ final Subject subject; - public SubjectWrapper(OnSubscribeFunc func, Subject subject) { + public SubjectWrapper(Action1> func, Subject subject) { super(func); this.subject = subject; } @@ -708,10 +718,17 @@ public static CustomReplaySubject create(int maxSize) { protected final Func1 intermediateSelector; private CustomReplaySubject( - Observable.OnSubscribeFunc onSubscribe, + final Observable.OnSubscribeFunc onSubscribe, ReplayState state, Func1 intermediateSelector) { - super(onSubscribe); + super(new Action1>() { + + @Override + public void call(Operator o) { + //TODO hack from OnSubscribeFunc to Action0 + onSubscribe.onSubscribe(o); + } + }); this.state = state; this.intermediateSelector = intermediateSelector; } diff --git a/rxjava-core/src/main/java/rx/operators/OperationToObservableIterable.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableIterable.java deleted file mode 100644 index 1ae63c6143..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableIterable.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright 2014 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 rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Scheduler; -import rx.Subscription; -import rx.schedulers.ImmediateScheduler; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Action0; -import rx.util.functions.Action1; - -/** - * Converts an Iterable sequence into an Observable. - *

- * - *

- * You can convert any object that supports the Iterable interface into an Observable that emits - * each item in the object, with the toObservable operation. - */ -public final class OperationToObservableIterable { - - public static OnSubscribeFunc toObservableIterable(Iterable list, Scheduler scheduler) { - if (scheduler instanceof ImmediateScheduler) { - return new ToObservableIterable(list); - } else { - return new ToObservableIterableScheduled(list, scheduler); - } - } - - public static OnSubscribeFunc toObservableIterable(Iterable list) { - return new ToObservableIterable(list); - } - - private static class ToObservableIterableScheduled implements OnSubscribeFunc { - - public ToObservableIterableScheduled(Iterable list, Scheduler scheduler) { - this.iterable = list; - this.scheduler = scheduler; - } - - Scheduler scheduler; - final Iterable iterable; - - public Subscription onSubscribe(final Observer observer) { - final Iterator iterator = iterable.iterator(); - return scheduler.schedule(new Action1() { - @Override - public void call(Action0 self) { - try { - if (iterator.hasNext()) { - T x = iterator.next(); - observer.onNext(x); - self.call(); - } else { - observer.onCompleted(); - } - } catch (Exception e) { - observer.onError(e); - } - } - }); - } - } - - private static class ToObservableIterable implements OnSubscribeFunc { - - public ToObservableIterable(Iterable list) { - this.iterable = list; - } - - final Iterable iterable; - - public Subscription onSubscribe(final Observer observer) { - try { - for (T t : iterable) { - observer.onNext(t); - } - observer.onCompleted(); - } catch (Exception e) { - observer.onError(e); - } - return Subscriptions.empty(); - } - } -} diff --git a/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java deleted file mode 100644 index 550343587e..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableList.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2014 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 rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; - -/** - * Returns an Observable that emits a single item, a list composed of all the items emitted by the - * source Observable. - *

- * - *

- * Normally, an Observable that returns multiple items will do so by invoking its Observer's - * onNext method for each such item. You can change this behavior, instructing the - * Observable to compose a list of all of these multiple items and then to invoke the Observer's - * onNext method once, passing it the entire list, by using the toList operator. - *

- * Be careful not to use this operator on Observables that emit infinite or very large numbers of - * items, as you do not have the option to unsubscribe. - */ -public final class OperationToObservableList { - - public static OnSubscribeFunc> toObservableList(Observable that) { - return new ToObservableList(that); - } - - private static class ToObservableList implements OnSubscribeFunc> { - - private final Observable that; - - public ToObservableList(Observable that) { - this.that = that; - } - - public Subscription onSubscribe(final Observer> observer) { - - return that.subscribe(new Observer() { - final List list = new ArrayList(); - - public void onNext(T value) { - list.add(value); - } - - public void onError(Throwable ex) { - observer.onError(ex); - } - - public void onCompleted() { - try { - // 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(new ArrayList(list)); - observer.onCompleted(); - } catch (Throwable e) { - onError(e); - } - - } - }); - } - } -} diff --git a/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java b/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java deleted file mode 100644 index aaa42bf7b3..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperationToObservableSortedList.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright 2014 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.Comparator; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; - -import rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; -import rx.util.functions.Func2; - -/** - * Return an Observable that emits the items emitted by the source Observable, in a sorted order - * (each item emitted by the Observable must implement Comparable with respect to all other items - * in the sequence, or you must pass in a sort function). - *

- * - * - * @param - */ -public final class OperationToObservableSortedList { - - /** - * Sort T objects by their natural order (object must implement Comparable). - * - * @param sequence - * @throws ClassCastException - * if T objects do not implement Comparable - * @return an observable containing the sorted list - */ - public static OnSubscribeFunc> toSortedList(Observable sequence) { - return new ToObservableSortedList(sequence); - } - - /** - * Sort T objects using the defined sort function. - * - * @param sequence - * @param sortFunction - * @return an observable containing the sorted list - */ - public static OnSubscribeFunc> toSortedList(Observable sequence, Func2 sortFunction) { - return new ToObservableSortedList(sequence, sortFunction); - } - - private static class ToObservableSortedList implements OnSubscribeFunc> { - - private final Observable that; - private final ConcurrentLinkedQueue list = new ConcurrentLinkedQueue(); - private final Func2 sortFunction; - - // unchecked as we're support Object for the default - @SuppressWarnings("unchecked") - private ToObservableSortedList(Observable that) { - this(that, defaultSortFunction); - } - - private ToObservableSortedList(Observable that, Func2 sortFunction) { - this.that = that; - this.sortFunction = sortFunction; - } - - public Subscription onSubscribe(final Observer> observer) { - return that.subscribe(new Observer() { - public void onNext(T value) { - // onNext can be concurrently executed so list must be thread-safe - list.add(value); - } - - public void onError(Throwable ex) { - observer.onError(ex); - } - - public void onCompleted() { - try { - // copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface - ArrayList l = new ArrayList(list.size()); - for (T t : list) { - l.add(t); - } - - // sort the list before delivery - Collections.sort(l, new Comparator() { - - @Override - public int compare(T o1, T o2) { - return sortFunction.call(o1, o2); - } - - }); - - observer.onNext(Collections.unmodifiableList(l)); - observer.onCompleted(); - } catch (Throwable 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 { - - // unchecked because we want to support Object for this default - @SuppressWarnings("unchecked") - @Override - public Integer call(Object t1, Object t2) { - Comparable c1 = (Comparable) t1; - Comparable c2 = (Comparable) t2; - return c1.compareTo(c2); - } - - } - - } -} diff --git a/rxjava-core/src/main/java/rx/operators/OperationCast.java b/rxjava-core/src/main/java/rx/operators/OperatorCast.java similarity index 52% rename from rxjava-core/src/main/java/rx/operators/OperationCast.java rename to rxjava-core/src/main/java/rx/operators/OperatorCast.java index 6e5a9831e4..8d9ada0bdb 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCast.java +++ b/rxjava-core/src/main/java/rx/operators/OperatorCast.java @@ -15,21 +15,38 @@ */ package rx.operators; -import rx.Observable; -import rx.Observable.OnSubscribeFunc; +import rx.Operator; import rx.util.functions.Func1; /** * Converts the elements of an observable sequence to the specified type. */ -public class OperationCast { +public class OperatorCast implements Func1, Operator> { - public static OnSubscribeFunc cast( - Observable source, final Class klass) { - return OperationMap.map(source, new Func1() { - public R call(T t) { - return klass.cast(t); + private final Class castClass; + + public OperatorCast(Class castClass) { + this.castClass = castClass; + } + + @Override + public Operator call(final Operator o) { + return new Operator(o) { + + @Override + public void onCompleted() { + o.onCompleted(); + } + + @Override + public void onError(Throwable e) { + o.onError(e); + } + + @Override + public void onNext(T t) { + o.onNext(castClass.cast(t)); } - }); + }; } } diff --git a/rxjava-core/src/main/java/rx/operators/OperatorFromIterable.java b/rxjava-core/src/main/java/rx/operators/OperatorFromIterable.java new file mode 100644 index 0000000000..0cc8a6568a --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperatorFromIterable.java @@ -0,0 +1,48 @@ +/** + * Copyright 2014 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.Operator; +import rx.util.functions.Action1; + +/** + * Converts an Iterable sequence into an Observable. + *

+ * + *

+ * You can convert any object that supports the Iterable interface into an Observable that emits + * each item in the object, with the toObservable operation. + */ +public final class OperatorFromIterable implements Action1> { + + final Iterable is; + + public OperatorFromIterable(Iterable iterable) { + this.is = iterable; + } + + @Override + public void call(Operator o) { + for (T i : is) { + if (o.isUnsubscribed()) { + return; + } + o.onNext(i); + } + o.onCompleted(); + } + +} diff --git a/rxjava-core/src/main/java/rx/operators/OperatorGroupBy.java b/rxjava-core/src/main/java/rx/operators/OperatorGroupBy.java new file mode 100644 index 0000000000..cd5af4dad9 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperatorGroupBy.java @@ -0,0 +1,145 @@ +/** + * Copyright 2014 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.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import rx.Operator; +import rx.observables.GroupedObservable; +import rx.subjects.PublishSubject; +import rx.subscriptions.CompositeSubscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action0; +import rx.util.functions.Action1; +import rx.util.functions.Func1; + +/** + * Groups the items emitted by an Observable according to a specified criterion, and emits these + * grouped items as Observables, one Observable per group. + *

+ * + */ +public final class OperatorGroupBy implements Func1>, Operator> { + + final Func1 keySelector; + + public OperatorGroupBy(final Func1 keySelector) { + this.keySelector = keySelector; + } + + @Override + public Operator call(final Operator> childOperator) { + // a new CompositeSubscription to decouple the subscription as the inner subscriptions need a separate lifecycle + // and will unsubscribe on this parent if they are all unsubscribed + return new Operator(new CompositeSubscription()) { + private final Map> groups = new HashMap>(); + private final AtomicInteger completionCounter = new AtomicInteger(0); + + @Override + public void onCompleted() { + // if we receive onCompleted from our parent we onComplete children + for (PublishSubject ps : groups.values()) { + ps.onCompleted(); + } + + if (completionCounter.get() == 0) { + // special case if no children are running (such as an empty sequence, or just getting the groups and not subscribing) + childOperator.onCompleted(); + } + } + + @Override + public void onError(Throwable e) { + // we immediately tear everything down if we receive an error + childOperator.onError(e); + } + + @Override + public void onNext(T t) { + try { + final K key = keySelector.call(t); + PublishSubject gps = groups.get(key); + if (gps == null) { + // this group doesn't exist + if (childOperator.isUnsubscribed()) { + // we have been unsubscribed on the outer so won't send any more groups + return; + } + gps = PublishSubject.create(); + final PublishSubject _gps = gps; + + GroupedObservable go = new GroupedObservable(key, new Action1>() { + + @Override + public void call(final Operator o) { + // number of children we have running + completionCounter.incrementAndGet(); + o.add(Subscriptions.create(new Action0() { + + @Override + public void call() { + completeInner(); + } + + })); + _gps.subscribe(new Operator(o) { + + @Override + public void onCompleted() { + o.onCompleted(); + completeInner(); + } + + @Override + public void onError(Throwable e) { + o.onError(e); + } + + @Override + public void onNext(T t) { + o.onNext(t); + } + + }); + } + + }); + groups.put(key, gps); + childOperator.onNext(go); + } + // we have the correct group so send value to it + gps.onNext(t); + } catch (Throwable e) { + onError(e); + } + } + + private void completeInner() { + if (completionCounter.decrementAndGet() == 0) { + unsubscribe(); + for (PublishSubject ps : groups.values()) { + ps.onCompleted(); + } + childOperator.onCompleted(); + } + } + + }; + } + +} diff --git a/rxjava-core/src/main/java/rx/operators/OperatorMap.java b/rxjava-core/src/main/java/rx/operators/OperatorMap.java new file mode 100644 index 0000000000..afd0a30b35 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperatorMap.java @@ -0,0 +1,61 @@ +/** + * Copyright 2014 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.Operator; +import rx.util.functions.Func1; + +/** + * Applies a function of your choosing to every item emitted by an Observable, and returns this + * transformation as a new Observable. + *

+ * + */ +public final class OperatorMap implements Func1, Operator> { + + private final Func1 transformer; + + public OperatorMap(Func1 transformer) { + this.transformer = transformer; + } + + @Override + public Operator call(final Operator o) { + return new Operator(o) { + + @Override + public void onCompleted() { + o.onCompleted(); + } + + @Override + public void onError(Throwable e) { + o.onError(e); + } + + @Override + public void onNext(T t) { + try { + o.onNext(transformer.call(t)); + } catch (Throwable e) { + onError(e); + } + } + + }; + } + +} diff --git a/rxjava-core/src/main/java/rx/operators/OperatorMerge.java b/rxjava-core/src/main/java/rx/operators/OperatorMerge.java new file mode 100644 index 0000000000..e1f468f68f --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperatorMerge.java @@ -0,0 +1,133 @@ +/** + * Copyright 2014 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.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; + +import rx.Observable; +import rx.Observer; +import rx.Operator; +import rx.subscriptions.CompositeSubscription; +import rx.util.functions.Func1; + +/** + * Flattens a list of Observables into one Observable sequence, without any transformation. + *

+ * + *

+ * You can combine the items emitted by multiple Observables so that they act like a single + * Observable, by using the merge operation. + */ +public final class OperatorMerge implements Func1, Operator>> { + private final int maxConcurrent; + + public OperatorMerge() { + maxConcurrent = Integer.MAX_VALUE; + } + + public OperatorMerge(int maxConcurrent) { + if (maxConcurrent <= 0) { + throw new IllegalArgumentException("maxConcurrent must be positive"); + } + this.maxConcurrent = maxConcurrent; + } + + @Override + public Operator> call(final Operator outerOperation) { + + final AtomicInteger completionCounter = new AtomicInteger(1); + final AtomicInteger concurrentCounter = new AtomicInteger(1); + // Concurrent* since we'll be accessing them from the inner Observers which can be on other threads + final ConcurrentLinkedQueue> pending = new ConcurrentLinkedQueue>(); + + final Observer o = new SynchronizedObserver(outerOperation); + return new Operator>(outerOperation) { + + @Override + public void onCompleted() { + complete(); + } + + @Override + public void onError(Throwable e) { + o.onError(e); + } + + @Override + public void onNext(Observable innerObservable) { + // track so we send onComplete only when all have finished + completionCounter.incrementAndGet(); + // check concurrency + if (concurrentCounter.incrementAndGet() > maxConcurrent) { + pending.add(innerObservable); + concurrentCounter.decrementAndGet(); + } else { + // we are able to proceed + CompositeSubscription innerSubscription = new CompositeSubscription(); + outerOperation.add(innerSubscription); + innerObservable.subscribe(Operator.create(new InnerObserver(), innerSubscription)); + } + } + + private void complete() { + if (completionCounter.decrementAndGet() == 0) { + o.onCompleted(); + return; + } else { + // not all are completed and some may still need to run + concurrentCounter.decrementAndGet(); + } + + // do work-stealing on whatever thread we're on and subscribe to pending observables + if (concurrentCounter.incrementAndGet() > maxConcurrent) { + // still not space to run + concurrentCounter.decrementAndGet(); + } else { + // we can run + Observable outstandingObservable = pending.poll(); + if (outstandingObservable != null) { + CompositeSubscription innerSubscription = new CompositeSubscription(); + outerOperation.add(innerSubscription); + outstandingObservable.subscribe(Operator.create(new InnerObserver(), innerSubscription)); + } + } + } + + final class InnerObserver implements Observer { + + @Override + public void onCompleted() { + complete(); + } + + @Override + public void onError(Throwable e) { + o.onError(e); + } + + @Override + public void onNext(T a) { + o.onNext(a); + } + + }; + + }; + + } + +} diff --git a/rxjava-core/src/main/java/rx/operators/OperatorParallel.java b/rxjava-core/src/main/java/rx/operators/OperatorParallel.java new file mode 100644 index 0000000000..59a4a549c2 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperatorParallel.java @@ -0,0 +1,62 @@ +/** + * Copyright 2014 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.Operator; +import rx.Scheduler; +import rx.observables.GroupedObservable; +import rx.util.functions.Func1; + +/** + * Identifies unit of work that can be executed in parallel on a given Scheduler. + */ +public final class OperatorParallel implements Func1, Operator> { + + private final Scheduler scheduler; + private final Func1, Observable> f; + + public OperatorParallel(Func1, Observable> f, Scheduler scheduler) { + this.scheduler = scheduler; + this.f = f; + } + + @Override + public Operator call(Operator op) { + + Func1>, Operator> groupBy = new OperatorGroupBy(new Func1() { + + int i = 0; + + @Override + public Integer call(T t) { + return i++ % scheduler.degreeOfParallelism(); + } + + }); + + Func1>, Operator>> map = new OperatorMap, Observable>(new Func1, Observable>() { + + @Override + public Observable call(GroupedObservable g) { + return f.call(g.observeOn(scheduler)); + } + }); + + // bind together operators + return groupBy.call(map.call(new OperatorMerge().call(op))); + } +} diff --git a/rxjava-core/src/main/java/rx/operators/OperatorTake.java b/rxjava-core/src/main/java/rx/operators/OperatorTake.java new file mode 100644 index 0000000000..baccb05de6 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperatorTake.java @@ -0,0 +1,88 @@ +/** + * Copyright 2014 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.Operator; +import rx.subscriptions.CompositeSubscription; +import rx.util.functions.Func1; + +/** + * Returns an Observable that emits the first num items emitted by the source + * Observable. + *

+ * + *

+ * You can choose to pay attention only to the first num items emitted by an + * Observable by using the take operation. This operation returns an Observable that will invoke a + * subscribing Observer's onNext function a maximum of num times before + * invoking onCompleted. + */ +public final class OperatorTake implements Func1, Operator> { + + final int limit; + + public OperatorTake(int limit) { + this.limit = limit; + } + + @Override + public Operator call(final Operator o) { + CompositeSubscription parent = new CompositeSubscription(); + if (limit == 0) { + o.onCompleted(); + parent.unsubscribe(); + } + /* + * We decouple the parent and child subscription so there can be multiple take() in a chain + * such as for the groupBy operator use case where you may take(1) on groups and take(20) on the children. + * + * Thus, we only unsubscribe UPWARDS to the parent and an onComplete DOWNSTREAM. + */ + return new Operator(parent) { + + int count = 0; + boolean completed = false; + + @Override + public void onCompleted() { + if (!completed) { + o.onCompleted(); + } + } + + @Override + public void onError(Throwable e) { + if (!completed) { + o.onError(e); + } + } + + @Override + public void onNext(T i) { + if (!isUnsubscribed()) { + o.onNext(i); + if (++count >= limit) { + completed = true; + o.onCompleted(); + unsubscribe(); + } + } + } + + }; + } + +} diff --git a/rxjava-core/src/main/java/rx/operators/OperationTake.java b/rxjava-core/src/main/java/rx/operators/OperatorTakeTimed.java similarity index 99% rename from rxjava-core/src/main/java/rx/operators/OperationTake.java rename to rxjava-core/src/main/java/rx/operators/OperatorTakeTimed.java index 2d11d06761..6dd8fc0940 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTake.java +++ b/rxjava-core/src/main/java/rx/operators/OperatorTakeTimed.java @@ -38,8 +38,10 @@ * subscribing Observer's onNext function a maximum of num times before * invoking onCompleted. */ -public final class OperationTake { +public final class OperatorTakeTimed { + //TODO this has not been migrated to use bind yet + /** * Returns a specified number of contiguous values from the start of an observable sequence. * diff --git a/rxjava-core/src/main/java/rx/operators/OperationTimestamp.java b/rxjava-core/src/main/java/rx/operators/OperatorTimestamp.java similarity index 51% rename from rxjava-core/src/main/java/rx/operators/OperationTimestamp.java rename to rxjava-core/src/main/java/rx/operators/OperatorTimestamp.java index 9cd086841a..6a8bfe7499 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationTimestamp.java +++ b/rxjava-core/src/main/java/rx/operators/OperatorTimestamp.java @@ -15,8 +15,8 @@ */ package rx.operators; -import rx.Observable; -import rx.Observable.OnSubscribeFunc; +import rx.Observer; +import rx.Operator; import rx.Scheduler; import rx.util.Timestamped; import rx.util.functions.Func1; @@ -26,35 +26,37 @@ *

* */ -public final class OperationTimestamp { +public final class OperatorTimestamp implements Func1>, Operator> { + + private final Scheduler scheduler; + + public OperatorTimestamp(Scheduler scheduler) { + this.scheduler = scheduler; + } /** - * Accepts a sequence and adds timestamps to each item in it. - * - * @param sequence - * the input sequence. - * @param - * the type of the input sequence. * @return a sequence of timestamped values created by adding timestamps to each item in the input sequence. */ - public static OnSubscribeFunc> timestamp(Observable sequence) { - return OperationMap.map(sequence, new Func1>() { + @Override + public Operator call(final Operator> o) { + return new Operator(o) { + @Override - public Timestamped call(T value) { - return new Timestamped(System.currentTimeMillis(), value); + public void onCompleted() { + o.onCompleted(); + } + + @Override + public void onError(Throwable e) { + o.onError(e); } - }); - } - /** - * Timestamp the source elements based on the timing provided by the scheduler. - */ - public static OnSubscribeFunc> timestamp(Observable source, final Scheduler scheduler) { - return OperationMap.map(source, new Func1>() { @Override - public Timestamped call(T value) { - return new Timestamped(scheduler.now(), value); + public void onNext(T t) { + o.onNext(new Timestamped(scheduler.now(), t)); } - }); + + }; } + } diff --git a/rxjava-core/src/main/java/rx/operators/OperatorToObservableList.java b/rxjava-core/src/main/java/rx/operators/OperatorToObservableList.java new file mode 100644 index 0000000000..572599f329 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperatorToObservableList.java @@ -0,0 +1,69 @@ +/** + * Copyright 2014 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 rx.Operator; +import rx.util.functions.Func1; + +/** + * Returns an Observable that emits a single item, a list composed of all the items emitted by the + * source Observable. + *

+ * + *

+ * Normally, an Observable that returns multiple items will do so by invoking its Observer's + * onNext method for each such item. You can change this behavior, instructing the + * Observable to compose a list of all of these multiple items and then to invoke the Observer's + * onNext method once, passing it the entire list, by using the toList operator. + *

+ * Be careful not to use this operator on Observables that emit infinite or very large numbers of + * items, as you do not have the option to unsubscribe. + */ +public final class OperatorToObservableList implements Func1>, Operator> { + + @Override + public Operator call(final Operator> o) { + return new Operator(o) { + + final List list = new ArrayList(); + + @Override + public void onCompleted() { + try { + o.onNext(new ArrayList(list)); + o.onCompleted(); + } catch (Throwable e) { + onError(e); + } + } + + @Override + public void onError(Throwable e) { + o.onError(e); + } + + @Override + public void onNext(T value) { + list.add(value); + } + + }; + } + +} diff --git a/rxjava-core/src/main/java/rx/operators/OperatorToObservableSortedList.java b/rxjava-core/src/main/java/rx/operators/OperatorToObservableSortedList.java new file mode 100644 index 0000000000..64a6fce347 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperatorToObservableSortedList.java @@ -0,0 +1,104 @@ +/** + * Copyright 2014 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.Comparator; +import java.util.List; + +import rx.Operator; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +/** + * Return an Observable that emits the items emitted by the source Observable, in a sorted order + * (each item emitted by the Observable must implement Comparable with respect to all other items + * in the sequence, or you must pass in a sort function). + *

+ * + * + * @param + */ +public final class OperatorToObservableSortedList implements Func1>, Operator> { + private final Func2 sortFunction; + + @SuppressWarnings("unchecked") + public OperatorToObservableSortedList() { + this.sortFunction = defaultSortFunction; + } + + public OperatorToObservableSortedList(Func2 sortFunction) { + this.sortFunction = sortFunction; + } + + @Override + public Operator call(final Operator> o) { + return new Operator(o) { + + final List list = new ArrayList(); + + @Override + public void onCompleted() { + try { + + // sort the list before delivery + Collections.sort(list, new Comparator() { + + @Override + public int compare(T o1, T o2) { + return sortFunction.call(o1, o2); + } + + }); + + o.onNext(Collections.unmodifiableList(list)); + o.onCompleted(); + } catch (Throwable e) { + onError(e); + } + } + + @Override + public void onError(Throwable e) { + o.onError(e); + } + + @Override + public void onNext(T value) { + list.add(value); + } + + }; + } + + // raw because we want to support Object for this default + @SuppressWarnings("rawtypes") + private static Func2 defaultSortFunction = new DefaultComparableFunction(); + + private static class DefaultComparableFunction implements Func2 { + + // unchecked because we want to support Object for this default + @SuppressWarnings("unchecked") + @Override + public Integer call(Object t1, Object t2) { + Comparable c1 = (Comparable) t1; + Comparable c2 = (Comparable) t2; + return c1.compareTo(c2); + } + + } +} diff --git a/rxjava-core/src/main/java/rx/plugins/RxJavaObservableExecutionHook.java b/rxjava-core/src/main/java/rx/plugins/RxJavaObservableExecutionHook.java index 27c9178058..09319766da 100644 --- a/rxjava-core/src/main/java/rx/plugins/RxJavaObservableExecutionHook.java +++ b/rxjava-core/src/main/java/rx/plugins/RxJavaObservableExecutionHook.java @@ -18,7 +18,9 @@ import rx.Observable; import rx.Observable.OnSubscribeFunc; import rx.Observer; +import rx.Operator; import rx.Subscription; +import rx.util.functions.Action1; import rx.util.functions.Func1; /** @@ -52,6 +54,11 @@ public OnSubscribeFunc onSubscribeStart(Observable observabl // pass-thru by default return onSubscribe; } + + public Action1> onSubscribeStart(Observable observableInstance, final Action1> onSubscribe) { + // pass-thru by default + return onSubscribe; + } /** * Invoked after successful execution of {@link Observable#subscribe(rx.Observer)} with returned {@link Subscription}. diff --git a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java index 0a37bbb14d..7fdfba3662 100644 --- a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java @@ -20,6 +20,7 @@ import rx.Notification; import rx.Observer; +import rx.Operator; import rx.subjects.SubjectSubscriptionManager.SubjectObserver; import rx.util.functions.Action1; @@ -58,7 +59,7 @@ public static AsyncSubject create() { final SubjectSubscriptionManager subscriptionManager = new SubjectSubscriptionManager(); final AtomicReference> lastNotification = new AtomicReference>(new Notification()); - OnSubscribeFunc onSubscribe = subscriptionManager.getOnSubscribeFunc( + Action1> onSubscribe = subscriptionManager.getOnSubscribeFunc( /** * This function executes at beginning of subscription. * @@ -97,7 +98,7 @@ protected static void emitValueToObserver(Notification n, Observer subscriptionManager; final AtomicReference> lastNotification; - protected AsyncSubject(OnSubscribeFunc onSubscribe, SubjectSubscriptionManager subscriptionManager, AtomicReference> lastNotification) { + protected AsyncSubject(Action1> onSubscribe, SubjectSubscriptionManager subscriptionManager, AtomicReference> lastNotification) { super(onSubscribe); this.subscriptionManager = subscriptionManager; this.lastNotification = lastNotification; diff --git a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java index f6bbd7487c..a3fb7d0497 100644 --- a/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/BehaviorSubject.java @@ -20,8 +20,10 @@ import rx.Notification; import rx.Observer; +import rx.Operator; import rx.subjects.SubjectSubscriptionManager.SubjectObserver; import rx.util.functions.Action1; +import rx.util.functions.Action2; /** * Subject that publishes the most recent and all subsequent events to each subscribed {@link Observer}. @@ -90,7 +92,7 @@ public static BehaviorSubject create(T defaultValue) { // set a default value so subscriptions will immediately receive this until a new notification is received final AtomicReference> lastNotification = new AtomicReference>(new Notification(defaultValue)); - OnSubscribeFunc onSubscribe = subscriptionManager.getOnSubscribeFunc( + Action1> onSubscribe = subscriptionManager.getOnSubscribeFunc( /** * This function executes at beginning of subscription. * @@ -132,7 +134,7 @@ public void call(SubjectObserver o) { private final SubjectSubscriptionManager subscriptionManager; final AtomicReference> lastNotification; - protected BehaviorSubject(OnSubscribeFunc onSubscribe, SubjectSubscriptionManager subscriptionManager, AtomicReference> lastNotification) { + protected BehaviorSubject(Action1> onSubscribe, SubjectSubscriptionManager subscriptionManager, AtomicReference> lastNotification) { super(onSubscribe); this.subscriptionManager = subscriptionManager; this.lastNotification = lastNotification; diff --git a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java index 28e974ad8a..2e5d2487e6 100644 --- a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java @@ -20,8 +20,10 @@ import rx.Notification; import rx.Observer; +import rx.Operator; import rx.subjects.SubjectSubscriptionManager.SubjectObserver; import rx.util.functions.Action1; +import rx.util.functions.Action2; /** * Subject that, once and {@link Observer} has subscribed, publishes all subsequent events to the subscriber. @@ -53,7 +55,7 @@ public static PublishSubject create() { // set a default value so subscriptions will immediately receive this until a new notification is received final AtomicReference> lastNotification = new AtomicReference>(); - OnSubscribeFunc onSubscribe = subscriptionManager.getOnSubscribeFunc( + Action1> onSubscribe = subscriptionManager.getOnSubscribeFunc( /** * This function executes at beginning of subscription. * @@ -87,7 +89,7 @@ public void call(SubjectObserver o) { private final SubjectSubscriptionManager subscriptionManager; final AtomicReference> lastNotification; - protected PublishSubject(OnSubscribeFunc onSubscribe, SubjectSubscriptionManager subscriptionManager, AtomicReference> lastNotification) { + protected PublishSubject(Action1> onSubscribe, SubjectSubscriptionManager subscriptionManager, AtomicReference> lastNotification) { super(onSubscribe); this.subscriptionManager = subscriptionManager; this.lastNotification = lastNotification; diff --git a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java index 7243b3417f..525980b339 100644 --- a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java +++ b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java @@ -23,8 +23,10 @@ import rx.Notification; import rx.Observer; +import rx.Operator; import rx.subjects.SubjectSubscriptionManager.SubjectObserver; import rx.util.functions.Action1; +import rx.util.functions.Action2; /** * Subject that retains all events and will replay them to an {@link Observer} that subscribes. @@ -58,7 +60,7 @@ public static ReplaySubject create(int initialCapacity) { final SubjectSubscriptionManager subscriptionManager = new SubjectSubscriptionManager(); final ReplayState state = new ReplayState(initialCapacity); - OnSubscribeFunc onSubscribe = subscriptionManager.getOnSubscribeFunc( + Action1> onSubscribe = subscriptionManager.getOnSubscribeFunc( /** * This function executes at beginning of subscription. * We want to replay history with the subscribing thread @@ -107,7 +109,7 @@ public ReplayState(int initialCapacity) { private final SubjectSubscriptionManager subscriptionManager; private final ReplayState state; - protected ReplaySubject(OnSubscribeFunc onSubscribe, SubjectSubscriptionManager subscriptionManager, ReplayState state) { + protected ReplaySubject(Action1> onSubscribe, SubjectSubscriptionManager subscriptionManager, ReplayState state) { super(onSubscribe); this.subscriptionManager = subscriptionManager; this.state = state; diff --git a/rxjava-core/src/main/java/rx/subjects/Subject.java b/rxjava-core/src/main/java/rx/subjects/Subject.java index e887ea123a..9f2e40d26a 100644 --- a/rxjava-core/src/main/java/rx/subjects/Subject.java +++ b/rxjava-core/src/main/java/rx/subjects/Subject.java @@ -17,9 +17,11 @@ import rx.Observable; import rx.Observer; +import rx.Operator; +import rx.util.functions.Action1; public abstract class Subject extends Observable implements Observer { - protected Subject(OnSubscribeFunc onSubscribe) { + protected Subject(Action1> onSubscribe) { super(onSubscribe); } } diff --git a/rxjava-core/src/main/java/rx/subjects/SubjectSubscriptionManager.java b/rxjava-core/src/main/java/rx/subjects/SubjectSubscriptionManager.java index 4c8eb1fa70..0d69608f42 100644 --- a/rxjava-core/src/main/java/rx/subjects/SubjectSubscriptionManager.java +++ b/rxjava-core/src/main/java/rx/subjects/SubjectSubscriptionManager.java @@ -20,12 +20,12 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; -import rx.Observable.OnSubscribeFunc; import rx.Observer; +import rx.Operator; import rx.Subscription; import rx.operators.SafeObservableSubscription; -import rx.subscriptions.Subscriptions; import rx.util.functions.Action1; +import rx.util.functions.Action2; /* package */class SubjectSubscriptionManager { @@ -39,11 +39,11 @@ * Only runs if Subject is in terminal state and the Observer ends up not being registered. * @return */ - public OnSubscribeFunc getOnSubscribeFunc(final Action1> onSubscribe, final Action1> onTerminated) { - return new OnSubscribeFunc() { + public Action1> getOnSubscribeFunc(final Action1> onSubscribe, final Action1> onTerminated) { + return new Action1>() { @Override - public Subscription onSubscribe(Observer actualObserver) { - SubjectObserver observer = new SubjectObserver(actualObserver); + public void call(Operator actualOperator) { + SubjectObserver observer = new SubjectObserver(actualOperator); // invoke onSubscribe logic if (onSubscribe != null) { onSubscribe.call(observer); @@ -52,12 +52,10 @@ public Subscription onSubscribe(Observer actualObserver) { State current; State newState = null; boolean addedObserver = false; - Subscription s; do { current = state.get(); if (current.terminated) { // we are terminated so don't need to do anything - s = Subscriptions.empty(); addedObserver = false; // break out and don't try to modify state newState = current; @@ -71,7 +69,7 @@ public Subscription onSubscribe(Observer actualObserver) { break; } else { final SafeObservableSubscription subscription = new SafeObservableSubscription(); - s = subscription; + actualOperator.add(subscription); // add to parent if the Subject itself is unsubscribed addedObserver = true; subscription.wrap(new Subscription() { @Override @@ -97,13 +95,12 @@ public void unsubscribe() { if (newState.terminated && !addedObserver) { onTerminated.call(observer); } - - return s; } }; } + @SuppressWarnings({ "unchecked", "rawtypes" }) protected void terminate(Action1>> onTerminate) { State current; State newState = null; @@ -138,6 +135,7 @@ protected void terminate(Action1>> onTermi * * @return the array of current observers */ + @SuppressWarnings("unchecked") public SubjectObserver[] rawSnapshot() { return state.get().observers; } diff --git a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java index 8030000fb4..abb3b4c036 100644 --- a/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java +++ b/rxjava-core/src/main/java/rx/subscriptions/CompositeSubscription.java @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.concurrent.atomic.AtomicReference; import rx.Subscription; @@ -34,11 +33,13 @@ public final class CompositeSubscription implements Subscription { private final AtomicReference state = new AtomicReference(); + private static final State CLEAR_STATE = new State(false, new Subscription[0]); + private static final class State { final boolean isUnsubscribed; - final List subscriptions; + final Subscription[] subscriptions; - State(boolean u, List s) { + State(boolean u, Subscription[] s) { this.isUnsubscribed = u; this.subscriptions = s; } @@ -48,26 +49,32 @@ State unsubscribe() { } State add(Subscription s) { - List newSubscriptions = new ArrayList(); - newSubscriptions.addAll(subscriptions); - newSubscriptions.add(s); + Subscription[] newSubscriptions = Arrays.copyOf(subscriptions, subscriptions.length + 1); + newSubscriptions[newSubscriptions.length - 1] = s; return new State(isUnsubscribed, newSubscriptions); } State remove(Subscription s) { - List newSubscriptions = new ArrayList(); - newSubscriptions.addAll(subscriptions); - newSubscriptions.remove(s); // only first occurrence - return new State(isUnsubscribed, newSubscriptions); + ArrayList newSubscriptions = new ArrayList(subscriptions.length); + for (Subscription _s : subscriptions) { + if (!_s.equals(s)) { + newSubscriptions.add(_s); + } + } + return new State(isUnsubscribed, newSubscriptions.toArray(new Subscription[newSubscriptions.size()])); } State clear() { - return new State(isUnsubscribed, new ArrayList()); + return new State(isUnsubscribed, new Subscription[0]); } } + public CompositeSubscription() { + state.set(CLEAR_STATE); + } + public CompositeSubscription(final Subscription... subscriptions) { - state.set(new State(false, Arrays.asList(subscriptions))); + state.set(new State(false, subscriptions)); } public boolean isUnsubscribed() { @@ -133,7 +140,7 @@ public void unsubscribe() { unsubscribeFromAll(oldState.subscriptions); } - private static void unsubscribeFromAll(Collection subscriptions) { + private static void unsubscribeFromAll(Subscription[] subscriptions) { final Collection es = new ArrayList(); for (Subscription s : subscriptions) { try { diff --git a/rxjava-core/src/perf/java/rx/ObservableCreatePerformance.java b/rxjava-core/src/perf/java/rx/ObservableCreatePerformance.java new file mode 100644 index 0000000000..a3fb9aace4 --- /dev/null +++ b/rxjava-core/src/perf/java/rx/ObservableCreatePerformance.java @@ -0,0 +1,77 @@ +package rx; + +import rx.perf.AbstractPerformanceTester; +import rx.perf.LongSumObserver; +import rx.util.functions.Action0; +import rx.util.functions.Action1; + +public class ObservableCreatePerformance extends AbstractPerformanceTester { + + public static void main(String args[]) { + + final ObservableCreatePerformance spt = new ObservableCreatePerformance(); + try { + spt.runTest(new Action0() { + + @Override + public void call() { + spt.timeCreateAndSubscribe(); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + /** + * Observable.create(f).subscribe() + * + * With old OnSubscribeFunc implementation: + * + * --- With Subscriptions.empty(): + * + * Run: 10 - 19,339,367 ops/sec + * Run: 11 - 19,530,258 ops/sec + * Run: 12 - 19,616,843 ops/sec + * Run: 13 - 19,705,598 ops/sec + * Run: 14 - 19,314,042 ops/sec + * + * --- With CompositeSubscription(): + * + * Run: 10 - 11,407,321 ops/sec + * Run: 11 - 11,481,425 ops/sec + * Run: 12 - 11,215,188 ops/sec + * Run: 13 - 11,235,096 ops/sec + * Run: 14 - 11,269,688 ops/sec + * + * With new Action2 implementation (bind operator changes): + * + * --- always with CompositeSubscription(): + * + * Run: 10 - 15,846,002 ops/sec + * Run: 11 - 15,671,181 ops/sec + * Run: 12 - 15,401,580 ops/sec + * Run: 13 - 15,841,283 ops/sec + * Run: 14 - 15,317,970 ops/sec + */ + public long timeCreateAndSubscribe() { + + Observable s = Observable.create(new Action1>() { + + @Override + public void call(Operator o) { + o.onNext(1L); + o.onCompleted(); + } + + }); + LongSumObserver o = new LongSumObserver(); + + for (long l = 0; l < REPETITIONS; l++) { + s.subscribe(o); + } + return o.sum; + } + +} \ No newline at end of file diff --git a/rxjava-core/src/perf/java/rx/operators/OperatorFromIterablePerformance.java b/rxjava-core/src/perf/java/rx/operators/OperatorFromIterablePerformance.java index e011726960..50ba2ecefe 100644 --- a/rxjava-core/src/perf/java/rx/operators/OperatorFromIterablePerformance.java +++ b/rxjava-core/src/perf/java/rx/operators/OperatorFromIterablePerformance.java @@ -1,9 +1,11 @@ package rx.operators; import java.util.Arrays; +import java.util.Iterator; import rx.Observable; import rx.perf.AbstractPerformanceTester; +import rx.perf.IntegerSumObserver; import rx.perf.LongSumObserver; import rx.util.functions.Action0; @@ -17,7 +19,9 @@ public static void main(String args[]) { @Override public void call() { + // spt.timeRepetitionsEmission(); spt.timeTenLongs(); + // spt.time1000Longs(); } }); } catch (Exception e) { @@ -26,14 +30,95 @@ public void call() { } + /** + * Single Observable from an Iterable with REPETITIONS items. + * + * Run: 10 - 210,730,391 ops/sec + * Run: 11 - 137,608,366 ops/sec + * Run: 12 - 204,114,957 ops/sec + * Run: 13 - 217,561,569 ops/sec + * Run: 14 - 185,061,810 ops/sec + * + * For comparison, if we don't check for isUnsubscribed then we get this: + * + * Run: 10 - 243,546,030 ops/sec + * Run: 11 - 149,102,403 ops/sec + * Run: 12 - 250,325,423 ops/sec + * Run: 13 - 249,289,524 ops/sec + * Run: 14 - 266,965,668 ops/sec + * + * @return + */ + public long timeRepetitionsEmission() { + LongSumObserver o = new LongSumObserver(); + Observable.from(ITERABLE_OF_REPETITIONS).subscribe(o); + return o.sum; + } + /** * Observable.from(Iterable) * + * Run: 0 - 259,801 ops/sec + * Run: 1 - 263,876 ops/sec + * Run: 2 - 262,117 ops/sec + * Run: 3 - 260,515 ops/sec + * Run: 4 - 261,124 ops/sec + */ + public long time1000Longs() { + + Iterable i = new Iterable() { + + @Override + public Iterator iterator() { + return new Iterator() { + int count = 0; + + @Override + public boolean hasNext() { + return count <= 1000; + } + + @Override + public Integer next() { + return count++; + } + + @Override + public void remove() { + // do nothing + } + + }; + }; + }; + + Observable s = Observable.from(i); + IntegerSumObserver o = new IntegerSumObserver(); + + for (long l = 0; l < REPETITIONS; l++) { + s.subscribe(o); + } + return o.sum; + } + + /** + * Observable.from(Iterable) + * + * --- Old synchronous implementation with Subscriptions.empty() and no unsubscribe support + * * Run: 10 - 10,629,658 ops/sec * Run: 11 - 9,573,775 ops/sec * Run: 12 - 10,589,787 ops/sec * Run: 13 - 10,514,141 ops/sec * Run: 14 - 9,765,586 ops/sec + * + * --- New synchronous implementation with OperatorSubscription and unsubscribe support + * + * Run: 10 - 8,096,667 ops/sec + * Run: 11 - 8,382,131 ops/sec + * Run: 12 - 8,256,288 ops/sec + * Run: 13 - 8,139,703 ops/sec + * Run: 14 - 8,011,023 ops/sec */ public long timeTenLongs() { diff --git a/rxjava-core/src/perf/java/rx/operators/OperatorMapPerformance.java b/rxjava-core/src/perf/java/rx/operators/OperatorMapPerformance.java index 12d3f11aae..1afdfcdccd 100644 --- a/rxjava-core/src/perf/java/rx/operators/OperatorMapPerformance.java +++ b/rxjava-core/src/perf/java/rx/operators/OperatorMapPerformance.java @@ -28,11 +28,11 @@ public void call() { /** * Observable.from(1L).map((l) -> { l+1}) * - * Run: 10 - 7,377,982 ops/sec - * Run: 11 - 7,714,715 ops/sec - * Run: 12 - 7,783,579 ops/sec - * Run: 13 - 7,693,372 ops/sec - * Run: 14 - 7,567,777 ops/sec + * Run: 10 - 11,375,632 ops/sec + * Run: 11 - 11,390,325 ops/sec + * Run: 12 - 11,655,527 ops/sec + * Run: 13 - 11,528,440 ops/sec + * Run: 14 - 11,321,181 ops/sec */ public long timeMapPlusOne() { diff --git a/rxjava-core/src/perf/java/rx/operators/OperatorMergePerformance.java b/rxjava-core/src/perf/java/rx/operators/OperatorMergePerformance.java index 77da79cb76..1f55a1daa3 100644 --- a/rxjava-core/src/perf/java/rx/operators/OperatorMergePerformance.java +++ b/rxjava-core/src/perf/java/rx/operators/OperatorMergePerformance.java @@ -3,6 +3,8 @@ import rx.Observable; import rx.perf.AbstractPerformanceTester; import rx.perf.IntegerSumObserver; +import rx.perf.LongSumObserver; +import rx.schedulers.Schedulers; import rx.util.functions.Action0; public class OperatorMergePerformance extends AbstractPerformanceTester { @@ -15,8 +17,9 @@ public static void main(String args[]) { @Override public void call() { + spt.timeRepetitionsEmissionSynchronous(); // spt.timeMergeAandBwithSingleItems(); - spt.timeMergeAandBwith100Items(); + // spt.timeMergeAandBwith100Items(); } }); } catch (Exception e) { @@ -25,15 +28,69 @@ public void call() { } + /** + * Run: 10 - 32,609,617 ops/sec + * Run: 11 - 33,511,839 ops/sec + * Run: 12 - 34,768,096 ops/sec + * Run: 13 - 32,376,499 ops/sec + * Run: 14 - 33,166,835 ops/sec + */ + public long timeRepetitionsEmissionSynchronous() { + + Observable sA = Observable.from(ITERABLE_OF_REPETITIONS); + Observable sB = Observable.from(ITERABLE_OF_REPETITIONS); + Observable s = Observable.merge(sA, sB); + + LongSumObserver o = new LongSumObserver(); + s.subscribe(o); + return o.sum; + } + + /** + * Run: 10 - 7,911,392,405 ops/sec + * Run: 11 - 8,620,689,655 ops/sec + * Run: 12 - 8,333,333,333 ops/sec + * Run: 13 - 6,775,067,750 ops/sec + * Run: 14 - 9,074,410,163 ops/sec + */ + public long timeRepetitionsEmissionConcurrent() { + + Observable sA = Observable.from(ITERABLE_OF_REPETITIONS).subscribeOn(Schedulers.newThread()); + Observable sB = Observable.from(ITERABLE_OF_REPETITIONS).subscribeOn(Schedulers.newThread()); + Observable s = Observable.merge(sA, sB); + + LongSumObserver o = new LongSumObserver(); + s.subscribe(o); + return o.sum; + } + /** * Observable.merge(from(1), from(1)) * + * -- Old pre-bind + * * Run: 10 - 2,308,617 ops/sec * Run: 11 - 2,309,602 ops/sec * Run: 12 - 2,318,590 ops/sec * Run: 13 - 2,270,100 ops/sec * Run: 14 - 2,312,006 ops/sec * + * -- new post-bind create + * + * Run: 10 - 1,983,888 ops/sec + * Run: 11 - 1,963,829 ops/sec + * Run: 12 - 1,952,321 ops/sec + * Run: 13 - 1,936,031 ops/sec + * Run: 14 - 1,862,887 ops/sec + * + * -- new merge operator + * + * Run: 10 - 2,630,464 ops/sec + * Run: 11 - 2,627,986 ops/sec + * Run: 12 - 2,628,281 ops/sec + * Run: 13 - 2,617,781 ops/sec + * Run: 14 - 2,625,995 ops/sec + * */ public long timeMergeAandBwithSingleItems() { @@ -52,11 +109,28 @@ public long timeMergeAandBwithSingleItems() { /** * Observable.merge(range(0, 100), range(100, 200)) * + * -- Old pre-bind + * * Run: 10 - 340,049 ops/sec * Run: 11 - 339,059 ops/sec * Run: 12 - 348,899 ops/sec * Run: 13 - 350,953 ops/sec * Run: 14 - 352,228 ops/sec + * + * -- new post-bind create + * + * Run: 0 - 236,536 ops/sec + * Run: 1 - 254,272 ops/sec + * + * -- new merge operator + * + * Run: 0 - 266,204 ops/sec + * Run: 1 - 290,318 ops/sec + * Run: 2 - 285,908 ops/sec + * Run: 3 - 289,695 ops/sec + * Run: 4 - 281,689 ops/sec + * Run: 5 - 290,375 ops/sec + * Run: 6 - 287,271 ops/sec */ public long timeMergeAandBwith100Items() { diff --git a/rxjava-core/src/perf/java/rx/operators/OperatorTakePerformance.java b/rxjava-core/src/perf/java/rx/operators/OperatorTakePerformance.java index a762e7853b..04365ef4aa 100644 --- a/rxjava-core/src/perf/java/rx/operators/OperatorTakePerformance.java +++ b/rxjava-core/src/perf/java/rx/operators/OperatorTakePerformance.java @@ -27,11 +27,11 @@ public void call() { /** * Observable.range(0, 10).take(5); * - * Run: 10 - 3,951,557 ops/sec - * Run: 11 - 3,981,329 ops/sec - * Run: 12 - 3,988,949 ops/sec - * Run: 13 - 3,925,971 ops/sec - * Run: 14 - 4,033,468 ops/sec + * Run: 10 - 7,936,306 ops/sec + * Run: 11 - 8,220,209 ops/sec + * Run: 12 - 7,783,276 ops/sec + * Run: 13 - 8,435,373 ops/sec + * Run: 14 - 7,894,454 ops/sec */ public long timeTake5() { diff --git a/rxjava-core/src/perf/java/rx/operators/OperatorZipPerformance.java b/rxjava-core/src/perf/java/rx/operators/OperatorZipPerformance.java index 2644ee42cd..12781f9610 100644 --- a/rxjava-core/src/perf/java/rx/operators/OperatorZipPerformance.java +++ b/rxjava-core/src/perf/java/rx/operators/OperatorZipPerformance.java @@ -16,8 +16,8 @@ public static void main(String args[]) { @Override public void call() { - // spt.timeZipAandBwithSingleItems(); - spt.timeZipAandBwith100Items(); + spt.timeZipAandBwithSingleItems(); + // spt.timeZipAandBwith100Items(); } }); } catch (Exception e) { diff --git a/rxjava-core/src/perf/java/rx/perf/AbstractPerformanceTester.java b/rxjava-core/src/perf/java/rx/perf/AbstractPerformanceTester.java index 4cc9b6b664..56d92278ab 100644 --- a/rxjava-core/src/perf/java/rx/perf/AbstractPerformanceTester.java +++ b/rxjava-core/src/perf/java/rx/perf/AbstractPerformanceTester.java @@ -1,5 +1,7 @@ package rx.perf; +import java.util.Iterator; + import rx.util.functions.Action0; public abstract class AbstractPerformanceTester { @@ -44,4 +46,30 @@ public long baseline() { return o.sum; } + public static Iterable ITERABLE_OF_REPETITIONS = new Iterable() { + + @Override + public Iterator iterator() { + return new Iterator() { + long count = 0; + + @Override + public boolean hasNext() { + return count <= REPETITIONS; + } + + @Override + public Long next() { + return count++; + } + + @Override + public void remove() { + // do nothing + } + + }; + }; + }; + } diff --git a/rxjava-core/src/perf/java/rx/schedulers/TestRecursionMemoryUsage.java b/rxjava-core/src/perf/java/rx/schedulers/TestRecursionMemoryUsage.java index 76d48e2ff7..cfa723006d 100644 --- a/rxjava-core/src/perf/java/rx/schedulers/TestRecursionMemoryUsage.java +++ b/rxjava-core/src/perf/java/rx/schedulers/TestRecursionMemoryUsage.java @@ -38,8 +38,8 @@ public static void main(String args[]) { usingFunc2(Schedulers.currentThread()); usingAction0(Schedulers.currentThread()); - usingFunc2(Schedulers.threadPoolForComputation()); - usingAction0(Schedulers.threadPoolForComputation()); + usingFunc2(Schedulers.computation()); + usingAction0(Schedulers.computation()); } protected static void usingFunc2(final Scheduler scheduler) { diff --git a/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java b/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java deleted file mode 100644 index 9b4230ca68..0000000000 --- a/rxjava-core/src/test/java/rx/operators/OperationGroupByTest.java +++ /dev/null @@ -1,347 +0,0 @@ -/** - * Copyright 2014 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 static org.junit.Assert.*; -import static rx.operators.OperationGroupBy.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; - -import rx.Observable; -import rx.Observer; -import rx.Subscription; -import rx.observables.GroupedObservable; -import rx.subscriptions.BooleanSubscription; -import rx.subscriptions.Subscriptions; -import rx.util.functions.Action1; -import rx.util.functions.Func1; - -public class OperationGroupByTest { - - final Func1 length = new Func1() { - @Override - public Integer call(String s) { - return s.length(); - } - }; - - @Test - public void testGroupBy() { - Observable source = Observable.from("one", "two", "three", "four", "five", "six"); - Observable> grouped = Observable.create(groupBy(source, length)); - - Map> map = toMap(grouped); - - assertEquals(3, map.size()); - assertArrayEquals(Arrays.asList("one", "two", "six").toArray(), map.get(3).toArray()); - assertArrayEquals(Arrays.asList("four", "five").toArray(), map.get(4).toArray()); - assertArrayEquals(Arrays.asList("three").toArray(), map.get(5).toArray()); - } - - @Test - public void testEmpty() { - Observable source = Observable.empty(); - Observable> grouped = Observable.create(groupBy(source, length)); - - Map> map = toMap(grouped); - - assertTrue(map.isEmpty()); - } - - @Test - public void testError() { - Observable sourceStrings = Observable.from("one", "two", "three", "four", "five", "six"); - Observable errorSource = Observable.error(new RuntimeException("forced failure")); - Observable source = Observable.concat(sourceStrings, errorSource); - - Observable> grouped = Observable.create(groupBy(source, length)); - - final AtomicInteger groupCounter = new AtomicInteger(); - final AtomicInteger eventCounter = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); - - grouped.mapMany(new Func1, Observable>() { - - @Override - public Observable call(final GroupedObservable o) { - groupCounter.incrementAndGet(); - return o.map(new Func1() { - - @Override - public String call(String v) { - return "Event => key: " + o.getKey() + " value: " + v; - } - }); - } - }).subscribe(new Observer() { - - @Override - public void onCompleted() { - - } - - @Override - public void onError(Throwable e) { - e.printStackTrace(); - error.set(e); - } - - @Override - public void onNext(String v) { - eventCounter.incrementAndGet(); - System.out.println(v); - - } - }); - - assertEquals(3, groupCounter.get()); - assertEquals(6, eventCounter.get()); - assertNotNull(error.get()); - } - - private static Map> toMap(Observable> observable) { - - final ConcurrentHashMap> result = new ConcurrentHashMap>(); - - observable.toBlockingObservable().forEach(new Action1>() { - - @Override - public void call(final GroupedObservable o) { - result.put(o.getKey(), new ConcurrentLinkedQueue()); - o.subscribe(new Action1() { - - @Override - public void call(V v) { - result.get(o.getKey()).add(v); - } - - }); - } - }); - - return result; - } - - /** - * Assert that only a single subscription to a stream occurs and that all events are received. - * - * @throws Throwable - */ - @Test - public void testGroupedEventStream() throws Throwable { - - final AtomicInteger eventCounter = new AtomicInteger(); - final AtomicInteger subscribeCounter = new AtomicInteger(); - final AtomicInteger groupCounter = new AtomicInteger(); - final CountDownLatch latch = new CountDownLatch(1); - final int count = 100; - final int groupCount = 2; - - Observable es = Observable.create(new Observable.OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(final Observer observer) { - System.out.println("*** Subscribing to EventStream ***"); - subscribeCounter.incrementAndGet(); - new Thread(new Runnable() { - - @Override - public void run() { - for (int i = 0; i < count; i++) { - Event e = new Event(); - e.source = i % groupCount; - e.message = "Event-" + i; - observer.onNext(e); - } - observer.onCompleted(); - } - - }).start(); - return Subscriptions.empty(); - } - - }); - - es.groupBy(new Func1() { - - @Override - public Integer call(Event e) { - return e.source; - } - }).mapMany(new Func1, Observable>() { - - @Override - public Observable call(GroupedObservable eventGroupedObservable) { - System.out.println("GroupedObservable Key: " + eventGroupedObservable.getKey()); - groupCounter.incrementAndGet(); - - return eventGroupedObservable.map(new Func1() { - - @Override - public String call(Event event) { - return "Source: " + event.source + " Message: " + event.message; - } - }); - - } - }).subscribe(new Observer() { - - @Override - public void onCompleted() { - latch.countDown(); - } - - @Override - public void onError(Throwable e) { - e.printStackTrace(); - latch.countDown(); - } - - @Override - public void onNext(String outputMessage) { - System.out.println(outputMessage); - eventCounter.incrementAndGet(); - } - }); - - latch.await(5000, TimeUnit.MILLISECONDS); - assertEquals(1, subscribeCounter.get()); - assertEquals(groupCount, groupCounter.get()); - assertEquals(count, eventCounter.get()); - - } - - /* - * We will only take 1 group with 20 events from it and then unsubscribe. - */ - @Test - public void testUnsubscribe() throws InterruptedException { - - final AtomicInteger eventCounter = new AtomicInteger(); - final AtomicInteger subscribeCounter = new AtomicInteger(); - final AtomicInteger groupCounter = new AtomicInteger(); - final AtomicInteger sentEventCounter = new AtomicInteger(); - final CountDownLatch latch = new CountDownLatch(1); - final int count = 100; - final int groupCount = 2; - - Observable es = Observable.create(new Observable.OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(final Observer observer) { - final BooleanSubscription s = new BooleanSubscription(); - System.out.println("testUnsubscribe => *** Subscribing to EventStream ***"); - subscribeCounter.incrementAndGet(); - new Thread(new Runnable() { - - @Override - public void run() { - for (int i = 0; i < count; i++) { - if (s.isUnsubscribed()) { - break; - } - Event e = new Event(); - e.source = i % groupCount; - e.message = "Event-" + i; - observer.onNext(e); - sentEventCounter.incrementAndGet(); - } - observer.onCompleted(); - } - - }).start(); - return s; - } - - }); - - es.groupBy(new Func1() { - - @Override - public Integer call(Event e) { - return e.source; - } - }) - .take(1) // we want only the first group - .mapMany(new Func1, Observable>() { - - @Override - public Observable call(GroupedObservable eventGroupedObservable) { - System.out.println("testUnsubscribe => GroupedObservable Key: " + eventGroupedObservable.getKey()); - groupCounter.incrementAndGet(); - - return eventGroupedObservable - .take(20) // limit to only 20 events on this group - .map(new Func1() { - - @Override - public String call(Event event) { - return "testUnsubscribe => Source: " + event.source + " Message: " + event.message; - } - }); - - } - }).subscribe(new Observer() { - - @Override - public void onCompleted() { - latch.countDown(); - } - - @Override - public void onError(Throwable e) { - e.printStackTrace(); - latch.countDown(); - } - - @Override - public void onNext(String outputMessage) { - System.out.println(outputMessage); - eventCounter.incrementAndGet(); - } - }); - - latch.await(5000, TimeUnit.MILLISECONDS); - assertEquals(1, subscribeCounter.get()); - assertEquals(1, groupCounter.get()); - assertEquals(20, eventCounter.get()); - // sentEvents will go until 'eventCounter' hits 20 and then unsubscribes - // which means it will also send (but ignore) the 19/20 events for the other group - // It will not however send all 100 events. - assertEquals(39, sentEventCounter.get(), 10); - // gave it a delta of 10 to account for the threading/unsubscription race condition which can vary depending on a machines performance, thread-scheduler, etc - } - - private static class Event { - int source; - String message; - - @Override - public String toString() { - return "Event => source: " + source + " message: " + message; - } - } -} diff --git a/rxjava-core/src/test/java/rx/operators/OperationCastTest.java b/rxjava-core/src/test/java/rx/operators/OperatorCastTest.java similarity index 84% rename from rxjava-core/src/test/java/rx/operators/OperationCastTest.java rename to rxjava-core/src/test/java/rx/operators/OperatorCastTest.java index e203b907a8..86412e259f 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationCastTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorCastTest.java @@ -16,20 +16,18 @@ package rx.operators; import static org.mockito.Mockito.*; -import static rx.operators.OperationCast.*; import org.junit.Test; import rx.Observable; import rx.Observer; -public class OperationCastTest { +public class OperatorCastTest { @Test public void testCast() { Observable source = Observable.from(1, 2); - Observable observable = Observable.create(cast(source, - Integer.class)); + Observable observable = source.cast(Integer.class); @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); @@ -44,8 +42,7 @@ public void testCast() { @Test public void testCastWithWrongType() { Observable source = Observable.from(1, 2); - Observable observable = Observable.create(cast(source, - Boolean.class)); + Observable observable = source.cast(Boolean.class); @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java b/rxjava-core/src/test/java/rx/operators/OperatorFromIterableTest.java similarity index 78% rename from rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java rename to rxjava-core/src/test/java/rx/operators/OperatorFromIterableTest.java index b75c8dd20d..47af0431b8 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableIterableTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorFromIterableTest.java @@ -17,7 +17,6 @@ import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; -import static rx.operators.OperationToObservableIterable.*; import java.util.Arrays; @@ -26,13 +25,12 @@ import rx.Observable; import rx.Observer; -import rx.schedulers.Schedulers; -public class OperationToObservableIterableTest { +public class OperatorFromIterableTest { @Test public void testIterable() { - Observable observable = Observable.create(toObservableIterable(Arrays. asList("one", "two", "three"))); + Observable observable = Observable.create(new OperatorFromIterable(Arrays. asList("one", "two", "three"))); @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); @@ -43,10 +41,10 @@ public void testIterable() { verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } - + @Test - public void testIterableScheduled() { - Observable observable = Observable.create(toObservableIterable(Arrays. asList("one", "two", "three"), Schedulers.currentThread())); + public void testObservableFromIterable() { + Observable observable = Observable.from(Arrays. asList("one", "two", "three")); @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); @@ -57,4 +55,5 @@ public void testIterableScheduled() { verify(aObserver, Mockito.never()).onError(any(Throwable.class)); verify(aObserver, times(1)).onCompleted(); } + } diff --git a/rxjava-core/src/test/java/rx/operators/OperatorGroupByTest.java b/rxjava-core/src/test/java/rx/operators/OperatorGroupByTest.java new file mode 100644 index 0000000000..35c8e9e107 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperatorGroupByTest.java @@ -0,0 +1,604 @@ +/** + * Copyright 2014 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 static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.Test; + +import rx.Observable; +import rx.Observer; +import rx.Operator; +import rx.Subscription; +import rx.observables.GroupedObservable; +import rx.schedulers.Schedulers; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action1; +import rx.util.functions.Func1; + +public class OperatorGroupByTest { + + final Func1 length = new Func1() { + @Override + public Integer call(String s) { + return s.length(); + } + }; + + @Test + public void testGroupBy() { + Observable source = Observable.from("one", "two", "three", "four", "five", "six"); + Observable> grouped = source.bind(new OperatorGroupBy(length)); + + Map> map = toMap(grouped); + + assertEquals(3, map.size()); + assertArrayEquals(Arrays.asList("one", "two", "six").toArray(), map.get(3).toArray()); + assertArrayEquals(Arrays.asList("four", "five").toArray(), map.get(4).toArray()); + assertArrayEquals(Arrays.asList("three").toArray(), map.get(5).toArray()); + } + + @Test + public void testEmpty() { + Observable source = Observable.empty(); + Observable> grouped = source.bind(new OperatorGroupBy(length)); + + Map> map = toMap(grouped); + + assertTrue(map.isEmpty()); + } + + @Test + public void testError() { + Observable sourceStrings = Observable.from("one", "two", "three", "four", "five", "six"); + Observable errorSource = Observable.error(new RuntimeException("forced failure")); + Observable source = Observable.concat(sourceStrings, errorSource); + + Observable> grouped = source.bind(new OperatorGroupBy(length)); + + final AtomicInteger groupCounter = new AtomicInteger(); + final AtomicInteger eventCounter = new AtomicInteger(); + final AtomicReference error = new AtomicReference(); + + grouped.flatMap(new Func1, Observable>() { + + @Override + public Observable call(final GroupedObservable o) { + groupCounter.incrementAndGet(); + return o.map(new Func1() { + + @Override + public String call(String v) { + return "Event => key: " + o.getKey() + " value: " + v; + } + }); + } + }).subscribe(new Observer() { + + @Override + public void onCompleted() { + + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + error.set(e); + } + + @Override + public void onNext(String v) { + eventCounter.incrementAndGet(); + System.out.println(v); + + } + }); + + assertEquals(3, groupCounter.get()); + assertEquals(6, eventCounter.get()); + assertNotNull(error.get()); + } + + private static Map> toMap(Observable> observable) { + + final ConcurrentHashMap> result = new ConcurrentHashMap>(); + + observable.toBlockingObservable().forEach(new Action1>() { + + @Override + public void call(final GroupedObservable o) { + result.put(o.getKey(), new ConcurrentLinkedQueue()); + o.subscribe(new Action1() { + + @Override + public void call(V v) { + result.get(o.getKey()).add(v); + } + + }); + } + }); + + return result; + } + + /** + * Assert that only a single subscription to a stream occurs and that all events are received. + * + * @throws Throwable + */ + @Test + public void testGroupedEventStream() throws Throwable { + + final AtomicInteger eventCounter = new AtomicInteger(); + final AtomicInteger subscribeCounter = new AtomicInteger(); + final AtomicInteger groupCounter = new AtomicInteger(); + final CountDownLatch latch = new CountDownLatch(1); + final int count = 100; + final int groupCount = 2; + + Observable es = Observable.create(new Observable.OnSubscribeFunc() { + + @Override + public Subscription onSubscribe(final Observer observer) { + System.out.println("*** Subscribing to EventStream ***"); + subscribeCounter.incrementAndGet(); + new Thread(new Runnable() { + + @Override + public void run() { + for (int i = 0; i < count; i++) { + Event e = new Event(); + e.source = i % groupCount; + e.message = "Event-" + i; + observer.onNext(e); + } + observer.onCompleted(); + } + + }).start(); + return Subscriptions.empty(); + } + + }); + + es.groupBy(new Func1() { + + @Override + public Integer call(Event e) { + return e.source; + } + }).flatMap(new Func1, Observable>() { + + @Override + public Observable call(GroupedObservable eventGroupedObservable) { + System.out.println("GroupedObservable Key: " + eventGroupedObservable.getKey()); + groupCounter.incrementAndGet(); + + return eventGroupedObservable.map(new Func1() { + + @Override + public String call(Event event) { + return "Source: " + event.source + " Message: " + event.message; + } + }); + + } + }).subscribe(new Observer() { + + @Override + public void onCompleted() { + latch.countDown(); + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + latch.countDown(); + } + + @Override + public void onNext(String outputMessage) { + System.out.println(outputMessage); + eventCounter.incrementAndGet(); + } + }); + + latch.await(5000, TimeUnit.MILLISECONDS); + assertEquals(1, subscribeCounter.get()); + assertEquals(groupCount, groupCounter.get()); + assertEquals(count, eventCounter.get()); + + } + + /* + * We will only take 1 group with 20 events from it and then unsubscribe. + */ + @Test + public void testUnsubscribeOnNestedTakeAndSyncInfiniteStream() throws InterruptedException { + final AtomicInteger subscribeCounter = new AtomicInteger(); + final AtomicInteger sentEventCounter = new AtomicInteger(); + doTestUnsubscribeOnNestedTakeAndAsyncInfiniteStream(SYNC_INFINITE_OBSERVABLE_OF_EVENT(2, subscribeCounter, sentEventCounter), subscribeCounter); + } + + /* + * We will only take 1 group with 20 events from it and then unsubscribe. + */ + @Test + public void testUnsubscribeOnNestedTakeAndAsyncInfiniteStream() throws InterruptedException { + final AtomicInteger subscribeCounter = new AtomicInteger(); + final AtomicInteger sentEventCounter = new AtomicInteger(); + doTestUnsubscribeOnNestedTakeAndAsyncInfiniteStream(ASYNC_INFINITE_OBSERVABLE_OF_EVENT(2, subscribeCounter, sentEventCounter), subscribeCounter); + } + + private void doTestUnsubscribeOnNestedTakeAndAsyncInfiniteStream(Observable es, AtomicInteger subscribeCounter) throws InterruptedException { + final AtomicInteger eventCounter = new AtomicInteger(); + final AtomicInteger groupCounter = new AtomicInteger(); + final CountDownLatch latch = new CountDownLatch(1); + + es.groupBy(new Func1() { + + @Override + public Integer call(Event e) { + return e.source; + } + }) + .take(1) // we want only the first group + .flatMap(new Func1, Observable>() { + + @Override + public Observable call(GroupedObservable eventGroupedObservable) { + // System.out.println("testUnsubscribe => GroupedObservable Key: " + eventGroupedObservable.getKey()); + groupCounter.incrementAndGet(); + + return eventGroupedObservable + .take(20) // limit to only 20 events on this group + .map(new Func1() { + + @Override + public String call(Event event) { + return "testUnsubscribe => Source: " + event.source + " Message: " + event.message; + } + }); + + } + }).subscribe(new Observer() { + + @Override + public void onCompleted() { + latch.countDown(); + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + latch.countDown(); + } + + @Override + public void onNext(String outputMessage) { + System.out.println(outputMessage); + eventCounter.incrementAndGet(); + } + }); + + if (!latch.await(2000, TimeUnit.MILLISECONDS)) { + fail("timed out so likely did not unsubscribe correctly"); + } + assertEquals(1, subscribeCounter.get()); + assertEquals(1, groupCounter.get()); + assertEquals(20, eventCounter.get()); + // sentEvents will go until 'eventCounter' hits 20 and then unsubscribes + // which means it will also send (but ignore) the 19/20 events for the other group + // It will not however send all 100 events. + } + + @Test + public void testUnsubscribeViaTakeOnGroupThenMergeAndTake() { + final AtomicInteger subscribeCounter = new AtomicInteger(); + final AtomicInteger sentEventCounter = new AtomicInteger(); + final AtomicInteger eventCounter = new AtomicInteger(); + + SYNC_INFINITE_OBSERVABLE_OF_EVENT(4, subscribeCounter, sentEventCounter) + .groupBy(new Func1() { + + @Override + public Integer call(Event e) { + return e.source; + } + }) + // take 2 of the 4 groups + .take(2) + .flatMap(new Func1, Observable>() { + + @Override + public Observable call(GroupedObservable eventGroupedObservable) { + return eventGroupedObservable + .map(new Func1() { + + @Override + public String call(Event event) { + return "testUnsubscribe => Source: " + event.source + " Message: " + event.message; + } + }); + + } + }) + .take(30).subscribe(new Action1() { + + @Override + public void call(String s) { + eventCounter.incrementAndGet(); + System.out.println("=> " + s); + } + + }); + + assertEquals(30, eventCounter.get()); + // we should send 28 additional events that are filtered out as they are in the groups we skip + assertEquals(58, sentEventCounter.get()); + } + + @Test + public void testUnsubscribeViaTakeOnGroupThenTakeOnInner() { + final AtomicInteger subscribeCounter = new AtomicInteger(); + final AtomicInteger sentEventCounter = new AtomicInteger(); + final AtomicInteger eventCounter = new AtomicInteger(); + + SYNC_INFINITE_OBSERVABLE_OF_EVENT(4, subscribeCounter, sentEventCounter) + .groupBy(new Func1() { + + @Override + public Integer call(Event e) { + return e.source; + } + }) + // take 2 of the 4 groups + .take(2) + .flatMap(new Func1, Observable>() { + + @Override + public Observable call(GroupedObservable eventGroupedObservable) { + int numToTake = 0; + if (eventGroupedObservable.getKey() == 1) { + numToTake = 10; + } else if (eventGroupedObservable.getKey() == 2) { + numToTake = 5; + } + return eventGroupedObservable + .take(numToTake) + .map(new Func1() { + + @Override + public String call(Event event) { + return "testUnsubscribe => Source: " + event.source + " Message: " + event.message; + } + }); + + } + }) + .subscribe(new Action1() { + + @Override + public void call(String s) { + eventCounter.incrementAndGet(); + System.out.println("=> " + s); + } + + }); + + assertEquals(15, eventCounter.get()); + // we should send 22 additional events that are filtered out as they are skipped while taking the 15 we want + assertEquals(37, sentEventCounter.get()); + } + + @Test + public void testUnsubscribeOnGroupViaOnlyTakeOnInner() { + final AtomicInteger subscribeCounter = new AtomicInteger(); + final AtomicInteger sentEventCounter = new AtomicInteger(); + final AtomicInteger eventCounter = new AtomicInteger(); + + SYNC_INFINITE_OBSERVABLE_OF_EVENT(4, subscribeCounter, sentEventCounter) + .groupBy(new Func1() { + + @Override + public Integer call(Event e) { + return e.source; + } + }) + .flatMap(new Func1, Observable>() { + + @Override + public Observable call(GroupedObservable eventGroupedObservable) { + int numToTake = 0; + if (eventGroupedObservable.getKey() == 1) { + numToTake = 10; + } else if (eventGroupedObservable.getKey() == 2) { + numToTake = 5; + } + return eventGroupedObservable + .take(numToTake) + .map(new Func1() { + + @Override + public String call(Event event) { + return "testUnsubscribe => Source: " + event.source + " Message: " + event.message; + } + }); + + } + }) + .subscribe(new Action1() { + + @Override + public void call(String s) { + eventCounter.incrementAndGet(); + System.out.println("=> " + s); + } + + }); + + assertEquals(15, eventCounter.get()); + // we should send 22 additional events that are filtered out as they are skipped while taking the 15 we want + assertEquals(37, sentEventCounter.get()); + } + + @Test + public void testStaggeredCompletion() throws InterruptedException { + final AtomicInteger eventCounter = new AtomicInteger(); + final CountDownLatch latch = new CountDownLatch(1); + Observable.range(0, 100) + .groupBy(new Func1() { + + @Override + public Integer call(Integer i) { + return i % 2; + } + }) + .flatMap(new Func1, Observable>() { + + @Override + public Observable call(GroupedObservable group) { + if (group.getKey() == 0) { + return group.observeOn(Schedulers.newThread()).map(new Func1() { + + @Override + public Integer call(Integer t) { + try { + Thread.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return t * 10; + } + + }); + } else { + return group.observeOn(Schedulers.newThread()); + } + } + }) + .subscribe(new Observer() { + + @Override + public void onCompleted() { + System.out.println("=> onCompleted"); + latch.countDown(); + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + latch.countDown(); + } + + @Override + public void onNext(Integer s) { + eventCounter.incrementAndGet(); + System.out.println("=> " + s); + } + }); + + if (!latch.await(2000, TimeUnit.MILLISECONDS)) { + fail("timed out"); + } + + assertEquals(100, eventCounter.get()); + } + + @Test + public void testCompletionIfInnerNotSubscribed() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicInteger eventCounter = new AtomicInteger(); + Observable.range(0, 100) + .groupBy(new Func1() { + + @Override + public Integer call(Integer i) { + return i % 2; + } + }) + .subscribe(new Observer>() { + + @Override + public void onCompleted() { + latch.countDown(); + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + latch.countDown(); + } + + @Override + public void onNext(GroupedObservable s) { + eventCounter.incrementAndGet(); + System.out.println("=> " + s); + } + }); + if (!latch.await(500, TimeUnit.MILLISECONDS)) { + fail("timed out - never got completion"); + } + assertEquals(2, eventCounter.get()); + } + + private static class Event { + int source; + String message; + + @Override + public String toString() { + return "Event => source: " + source + " message: " + message; + } + } + + Observable ASYNC_INFINITE_OBSERVABLE_OF_EVENT(final int numGroups, final AtomicInteger subscribeCounter, final AtomicInteger sentEventCounter) { + return SYNC_INFINITE_OBSERVABLE_OF_EVENT(numGroups, subscribeCounter, sentEventCounter).subscribeOn(Schedulers.newThread()); + }; + + Observable SYNC_INFINITE_OBSERVABLE_OF_EVENT(final int numGroups, final AtomicInteger subscribeCounter, final AtomicInteger sentEventCounter) { + return Observable.create(new Action1>() { + + @Override + public void call(final Operator op) { + subscribeCounter.incrementAndGet(); + int i = 0; + while (!op.isUnsubscribed()) { + i++; + Event e = new Event(); + e.source = i % numGroups; + e.message = "Event-" + i; + op.onNext(e); + sentEventCounter.incrementAndGet(); + } + op.onCompleted(); + } + + }); + }; + +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java b/rxjava-core/src/test/java/rx/operators/OperatorMapTest.java similarity index 67% rename from rxjava-core/src/test/java/rx/operators/OperationMapTest.java rename to rxjava-core/src/test/java/rx/operators/OperatorMapTest.java index bdcad69729..0cc5e0a1e5 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMapTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorMapTest.java @@ -18,7 +18,7 @@ import static org.junit.Assert.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; -import static rx.operators.OperationMap.*; +import static rx.operators.OperatorMap.*; import java.util.HashMap; import java.util.Map; @@ -36,7 +36,7 @@ import rx.util.functions.Func1; import rx.util.functions.Func2; -public class OperationMapTest { +public class OperatorMapTest { @Mock Observer stringObserver; @@ -61,7 +61,7 @@ public void testMap() { Map m2 = getMap("Two"); Observable> observable = Observable.from(m1, m2); - Observable m = Observable.create(map(observable, new Func1, String>() { + Observable m = observable.bind(new OperatorMap, String>(new Func1, String>() { @Override public String call(Map map) { @@ -77,40 +77,6 @@ public String call(Map map) { verify(stringObserver, times(1)).onCompleted(); } - @Test - public void testMapWithIndex() { - Observable w = Observable.from("a", "b", "c"); - Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX)); - m.subscribe(stringObserver); - InOrder inOrder = inOrder(stringObserver); - inOrder.verify(stringObserver, times(1)).onNext("a0"); - inOrder.verify(stringObserver, times(1)).onNext("b1"); - inOrder.verify(stringObserver, times(1)).onNext("c2"); - inOrder.verify(stringObserver, times(1)).onCompleted(); - verify(stringObserver, never()).onError(any(Throwable.class)); - } - - @Test - public void testMapWithIndexAndMultipleSubscribers() { - Observable w = Observable.from("a", "b", "c"); - Observable m = Observable.create(mapWithIndex(w, APPEND_INDEX)); - m.subscribe(stringObserver); - m.subscribe(stringObserver2); - InOrder inOrder = inOrder(stringObserver); - inOrder.verify(stringObserver, times(1)).onNext("a0"); - inOrder.verify(stringObserver, times(1)).onNext("b1"); - inOrder.verify(stringObserver, times(1)).onNext("c2"); - inOrder.verify(stringObserver, times(1)).onCompleted(); - verify(stringObserver, never()).onError(any(Throwable.class)); - - InOrder inOrder2 = inOrder(stringObserver2); - inOrder2.verify(stringObserver2, times(1)).onNext("a0"); - inOrder2.verify(stringObserver2, times(1)).onNext("b1"); - inOrder2.verify(stringObserver2, times(1)).onNext("c2"); - inOrder2.verify(stringObserver2, times(1)).onCompleted(); - verify(stringObserver2, never()).onError(any(Throwable.class)); - } - @Test public void testMapMany() { /* simulate a top-level async call which returns IDs */ @@ -134,12 +100,12 @@ public Observable call(Integer id) { } /* simulate kicking off the async call and performing a select on it to transform the data */ - return Observable.create(map(subObservable, new Func1, String>() { + return subObservable.map(new Func1, String>() { @Override public String call(Map map) { return map.get("firstName"); } - })); + }); } }); @@ -169,13 +135,13 @@ public void testMapMany2() { @Override public Observable call(Observable> o) { - return Observable.create(map(o, new Func1, String>() { + return o.map(new Func1, String>() { @Override public String call(Map map) { return map.get("firstName"); } - })); + }); } }); @@ -193,7 +159,7 @@ public String call(Map map) { @Test public void testMapWithError() { Observable w = Observable.from("one", "fail", "two", "three", "fail"); - Observable m = Observable.create(map(w, new Func1() { + Observable m = w.bind(new OperatorMap(new Func1() { @Override public String call(String s) { if ("fail".equals(s)) { @@ -211,51 +177,9 @@ public String call(String s) { verify(stringObserver, times(1)).onError(any(Throwable.class)); } - /** - * This is testing how unsubscribe behavior is handled when an error occurs in a user provided function - * and the source is unsubscribed from ... but ignores or can't receive the unsubscribe as it is synchronous. - */ - @Test - public void testMapContainingErrorWithSequenceThatDoesntUnsubscribe() { - Observable w = Observable.from("one", "fail", "two", "three", "fail"); - final AtomicInteger c1 = new AtomicInteger(); - final AtomicInteger c2 = new AtomicInteger(); - Observable m = Observable.create(map(w, new Func1() { - @Override - public String call(String s) { - if ("fail".equals(s)) - throw new RuntimeException("Forced Failure"); - System.out.println("BadMapper:" + s); - c1.incrementAndGet(); - return s; - } - })).map(new Func1() { - @Override - public String call(String s) { - System.out.println("SecondMapper:" + s); - c2.incrementAndGet(); - return s; - } - }); - - m.subscribe(stringObserver); - - verify(stringObserver, times(1)).onNext("one"); - verify(stringObserver, never()).onNext("two"); - verify(stringObserver, never()).onNext("three"); - verify(stringObserver, never()).onCompleted(); - verify(stringObserver, times(1)).onError(any(Throwable.class)); - - // We should have only returned 1 value: "one" - // Since the unsubscribe doesn't propagate, we will actually be sent all events and need - // to ignore all after the first failure. - assertEquals(1, c1.get()); - assertEquals(1, c2.get()); - } - @Test(expected = IllegalArgumentException.class) public void testMapWithIssue417() { - Observable.from(1).observeOn(Schedulers.threadPoolForComputation()) + Observable.from(1).observeOn(Schedulers.computation()) .map(new Func1() { public Integer call(Integer arg0) { throw new IllegalArgumentException("any error"); @@ -269,7 +193,7 @@ public void testMapWithErrorInFuncAndThreadPoolScheduler() throws InterruptedExc // If map does not handle it, the error will disappear. // so map needs to handle the error by itself. Observable m = Observable.from("one") - .observeOn(Schedulers.threadPoolForComputation()) + .observeOn(Schedulers.computation()) .map(new Func1() { public String call(String arg0) { throw new IllegalArgumentException("any error"); diff --git a/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java b/rxjava-core/src/test/java/rx/operators/OperatorMergeTest.java similarity index 94% rename from rxjava-core/src/test/java/rx/operators/OperationMergeTest.java rename to rxjava-core/src/test/java/rx/operators/OperatorMergeTest.java index 51dfe51edd..34df5b1699 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationMergeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorMergeTest.java @@ -18,7 +18,7 @@ import static org.junit.Assert.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; -import static rx.operators.OperationMerge.*; +import static rx.operators.OperatorMerge.*; import java.util.ArrayList; import java.util.Arrays; @@ -42,7 +42,7 @@ import rx.util.functions.Action0; import rx.util.functions.Action1; -public class OperationMergeTest { +public class OperatorMergeTest { @Mock Observer stringObserver; @@ -114,32 +114,6 @@ public void testMergeList() { verify(stringObserver, times(2)).onNext("hello"); } - @Test - public void testUnSubscribe() { - TestObservable tA = new TestObservable(); - TestObservable tB = new TestObservable(); - - Observable m = Observable.merge(Observable.create(tA), Observable.create(tB)); - Subscription s = m.subscribe(stringObserver); - - tA.sendOnNext("Aone"); - tB.sendOnNext("Bone"); - s.unsubscribe(); - tA.sendOnNext("Atwo"); - tB.sendOnNext("Btwo"); - tA.sendOnCompleted(); - tB.sendOnCompleted(); - - verify(stringObserver, never()).onError(any(Throwable.class)); - verify(stringObserver, times(1)).onNext("Aone"); - verify(stringObserver, times(1)).onNext("Bone"); - assertTrue(tA.unsubscribed); - assertTrue(tB.unsubscribed); - verify(stringObserver, never()).onNext("Atwo"); - verify(stringObserver, never()).onNext("Btwo"); - verify(stringObserver, never()).onCompleted(); - } - @Test public void testUnSubscribeObservableOfObservables() throws InterruptedException { @@ -185,7 +159,7 @@ public void run() { }); final AtomicInteger count = new AtomicInteger(); - Observable.create(merge(source)).take(6).toBlockingObservable().forEach(new Action1() { + Observable.merge(source).take(6).toBlockingObservable().forEach(new Action1() { @Override public void call(Long v) { diff --git a/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java b/rxjava-core/src/test/java/rx/operators/OperatorParallelTest.java similarity index 98% rename from rxjava-core/src/test/java/rx/operators/OperationParallelTest.java rename to rxjava-core/src/test/java/rx/operators/OperatorParallelTest.java index 5c392e6dc9..fe72a088e4 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationParallelTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorParallelTest.java @@ -25,7 +25,7 @@ import rx.util.functions.Action1; import rx.util.functions.Func1; -public class OperationParallelTest { +public class OperatorParallelTest { @Test public void testParallel() { diff --git a/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java b/rxjava-core/src/test/java/rx/operators/OperatorTakeTest.java similarity index 67% rename from rxjava-core/src/test/java/rx/operators/OperationTakeTest.java rename to rxjava-core/src/test/java/rx/operators/OperatorTakeTest.java index 921a462225..746c7bcfdc 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTakeTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorTakeTest.java @@ -18,29 +18,28 @@ import static org.junit.Assert.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; -import static rx.operators.OperationTake.*; -import java.util.concurrent.TimeUnit; +import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; import org.junit.Test; import org.mockito.InOrder; import rx.Observable; import rx.Observer; +import rx.Operator; import rx.Subscription; -import rx.operators.OperationSkipTest.CustomException; -import rx.schedulers.TestScheduler; -import rx.subjects.PublishSubject; import rx.subscriptions.Subscriptions; +import rx.util.functions.Action1; import rx.util.functions.Func1; -public class OperationTakeTest { +public class OperatorTakeTest { @Test public void testTake1() { - Observable w = Observable.from("one", "two", "three"); - Observable take = Observable.create(take(w, 2)); + Observable w = Observable.from(Arrays.asList("one", "two", "three")); + Observable take = w.bind(new OperatorTake(2)); @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); @@ -54,8 +53,8 @@ public void testTake1() { @Test public void testTake2() { - Observable w = Observable.from("one", "two", "three"); - Observable take = Observable.create(take(w, 1)); + Observable w = Observable.from(Arrays.asList("one", "two", "three")); + Observable take = w.bind(new OperatorTake(1)); @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); @@ -69,7 +68,7 @@ public void testTake2() { @Test(expected = IllegalArgumentException.class) public void testTakeWithError() { - Observable.from(1, 2, 3).take(1).map(new Func1() { + Observable.from(Arrays.asList(1, 2, 3)).take(1).map(new Func1() { public Integer call(Integer t1) { throw new IllegalArgumentException("some error"); } @@ -78,7 +77,7 @@ public Integer call(Integer t1) { @Test public void testTakeWithErrorHappeningInOnNext() { - Observable w = Observable.from(1, 2, 3).take(2).map(new Func1() { + Observable w = Observable.from(Arrays.asList(1, 2, 3)).take(2).map(new Func1() { public Integer call(Integer t1) { throw new IllegalArgumentException("some error"); } @@ -94,7 +93,7 @@ public Integer call(Integer t1) { @Test public void testTakeWithErrorHappeningInTheLastOnNext() { - Observable w = Observable.from(1, 2, 3).take(1).map(new Func1() { + Observable w = Observable.from(Arrays.asList(1, 2, 3)).take(1).map(new Func1() { public Integer call(Integer t1) { throw new IllegalArgumentException("some error"); } @@ -122,7 +121,7 @@ public Subscription onSubscribe(Observer observer) { @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); - Observable.create(take(source, 1)).subscribe(aObserver); + source.bind(new OperatorTake(1)).subscribe(aObserver); verify(aObserver, times(1)).onNext("one"); // even though onError is called we take(1) so shouldn't see it @@ -152,7 +151,7 @@ public void unsubscribe() { @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); - Observable.create(take(source, 0)).subscribe(aObserver); + source.bind(new OperatorTake(0)).subscribe(aObserver); assertTrue("source subscribed", subscribed.get()); assertTrue("source unsubscribed", unSubscribed.get()); @@ -171,7 +170,7 @@ public void testUnsubscribeAfterTake() { @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); - Observable take = Observable.create(take(w, 1)); + Observable take = w.bind(new OperatorTake(1)); take.subscribe(aObserver); // wait for the Observable to complete @@ -191,6 +190,20 @@ public void testUnsubscribeAfterTake() { verifyNoMoreInteractions(aObserver); } + @Test(timeout = 2000) + public void testUnsubscribeFromSynchronousInfiniteObservable() { + final AtomicLong count = new AtomicLong(); + INFINITE_OBSERVABLE.take(10).subscribe(new Action1() { + + @Override + public void call(Long l) { + count.set(l); + } + + }); + assertEquals(10, count.get()); + } + private static class TestObservableFunc implements Observable.OnSubscribeFunc { final Subscription s; @@ -229,98 +242,16 @@ public void run() { } } - @Test - public void testTakeTimed() { - TestScheduler scheduler = new TestScheduler(); - - PublishSubject source = PublishSubject.create(); - - Observable result = source.take(1, TimeUnit.SECONDS, scheduler); - - Observer o = mock(Observer.class); - - result.subscribe(o); - - source.onNext(1); - source.onNext(2); - source.onNext(3); - - scheduler.advanceTimeBy(1, TimeUnit.SECONDS); - - source.onNext(4); - - InOrder inOrder = inOrder(o); - inOrder.verify(o).onNext(1); - inOrder.verify(o).onNext(2); - inOrder.verify(o).onNext(3); - inOrder.verify(o).onCompleted(); - inOrder.verifyNoMoreInteractions(); - - verify(o, never()).onNext(4); - verify(o, never()).onError(any(Throwable.class)); - } - - @Test - public void testTakeTimedErrorBeforeTime() { - TestScheduler scheduler = new TestScheduler(); - - PublishSubject source = PublishSubject.create(); - - Observable result = source.take(1, TimeUnit.SECONDS, scheduler); - - Observer o = mock(Observer.class); - - result.subscribe(o); - - source.onNext(1); - source.onNext(2); - source.onNext(3); - source.onError(new CustomException()); - - scheduler.advanceTimeBy(1, TimeUnit.SECONDS); - - source.onNext(4); - - InOrder inOrder = inOrder(o); - inOrder.verify(o).onNext(1); - inOrder.verify(o).onNext(2); - inOrder.verify(o).onNext(3); - inOrder.verify(o).onError(any(CustomException.class)); - inOrder.verifyNoMoreInteractions(); - - verify(o, never()).onCompleted(); - verify(o, never()).onNext(4); - } - - @Test - public void testTakeTimedErrorAfterTime() { - TestScheduler scheduler = new TestScheduler(); - - PublishSubject source = PublishSubject.create(); + private static Observable INFINITE_OBSERVABLE = Observable.create(new Action1>() { - Observable result = source.take(1, TimeUnit.SECONDS, scheduler); - - Observer o = mock(Observer.class); - - result.subscribe(o); - - source.onNext(1); - source.onNext(2); - source.onNext(3); - - scheduler.advanceTimeBy(1, TimeUnit.SECONDS); - - source.onNext(4); - source.onError(new CustomException()); - - InOrder inOrder = inOrder(o); - inOrder.verify(o).onNext(1); - inOrder.verify(o).onNext(2); - inOrder.verify(o).onNext(3); - inOrder.verify(o).onCompleted(); - inOrder.verifyNoMoreInteractions(); + @Override + public void call(Operator op) { + long l = 1; + while (!op.isUnsubscribed()) { + op.onNext(l++); + } + op.onCompleted(); + } - verify(o, never()).onNext(4); - verify(o, never()).onError(any(CustomException.class)); - } + }); } diff --git a/rxjava-core/src/test/java/rx/operators/OperatorTakeTimedTest.java b/rxjava-core/src/test/java/rx/operators/OperatorTakeTimedTest.java new file mode 100644 index 0000000000..2c39468785 --- /dev/null +++ b/rxjava-core/src/test/java/rx/operators/OperatorTakeTimedTest.java @@ -0,0 +1,138 @@ +/** + * Copyright 2014 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 static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; +import static rx.operators.OperatorTake.*; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.Test; +import org.mockito.InOrder; + +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.operators.OperationSkipTest.CustomException; +import rx.schedulers.TestScheduler; +import rx.subjects.PublishSubject; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Func1; + +public class OperatorTakeTimedTest { + + @Test + public void testTakeTimed() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + Observable result = source.take(1, TimeUnit.SECONDS, scheduler); + + @SuppressWarnings("unchecked") + Observer o = mock(Observer.class); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + scheduler.advanceTimeBy(1, TimeUnit.SECONDS); + + source.onNext(4); + + InOrder inOrder = inOrder(o); + inOrder.verify(o).onNext(1); + inOrder.verify(o).onNext(2); + inOrder.verify(o).onNext(3); + inOrder.verify(o).onCompleted(); + inOrder.verifyNoMoreInteractions(); + + verify(o, never()).onNext(4); + verify(o, never()).onError(any(Throwable.class)); + } + + @Test + public void testTakeTimedErrorBeforeTime() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + Observable result = source.take(1, TimeUnit.SECONDS, scheduler); + + @SuppressWarnings("unchecked") + Observer o = mock(Observer.class); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + source.onError(new CustomException()); + + scheduler.advanceTimeBy(1, TimeUnit.SECONDS); + + source.onNext(4); + + InOrder inOrder = inOrder(o); + inOrder.verify(o).onNext(1); + inOrder.verify(o).onNext(2); + inOrder.verify(o).onNext(3); + inOrder.verify(o).onError(any(CustomException.class)); + inOrder.verifyNoMoreInteractions(); + + verify(o, never()).onCompleted(); + verify(o, never()).onNext(4); + } + + @Test + public void testTakeTimedErrorAfterTime() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + Observable result = source.take(1, TimeUnit.SECONDS, scheduler); + + @SuppressWarnings("unchecked") + Observer o = mock(Observer.class); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + scheduler.advanceTimeBy(1, TimeUnit.SECONDS); + + source.onNext(4); + source.onError(new CustomException()); + + InOrder inOrder = inOrder(o); + inOrder.verify(o).onNext(1); + inOrder.verify(o).onNext(2); + inOrder.verify(o).onNext(3); + inOrder.verify(o).onCompleted(); + inOrder.verifyNoMoreInteractions(); + + verify(o, never()).onNext(4); + verify(o, never()).onError(any(CustomException.class)); + } +} diff --git a/rxjava-core/src/test/java/rx/operators/OperationTimestampTest.java b/rxjava-core/src/test/java/rx/operators/OperatorTimestampTest.java similarity index 98% rename from rxjava-core/src/test/java/rx/operators/OperationTimestampTest.java rename to rxjava-core/src/test/java/rx/operators/OperatorTimestampTest.java index 8b4af6e3af..dd299d4367 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationTimestampTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorTimestampTest.java @@ -32,7 +32,7 @@ import rx.subjects.PublishSubject; import rx.util.Timestamped; -public class OperationTimestampTest { +public class OperatorTimestampTest { @Mock Observer observer; diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java b/rxjava-core/src/test/java/rx/operators/OperatorToObservableListTest.java similarity index 66% rename from rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java rename to rxjava-core/src/test/java/rx/operators/OperatorToObservableListTest.java index 42c21e2e31..0ddc843b11 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableListTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorToObservableListTest.java @@ -17,7 +17,7 @@ import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; -import static rx.operators.OperationToObservableList.*; +import static rx.operators.OperatorToObservableList.*; import java.util.Arrays; import java.util.List; @@ -28,12 +28,25 @@ import rx.Observable; import rx.Observer; -public class OperationToObservableListTest { +public class OperatorToObservableListTest { @Test public void testList() { - Observable w = Observable.from("one", "two", "three"); - Observable> observable = Observable.create(toObservableList(w)); + Observable w = Observable.from(Arrays.asList("one", "two", "three")); + Observable> observable = w.bind(new OperatorToObservableList()); + + @SuppressWarnings("unchecked") + Observer> aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext(Arrays.asList("one", "two", "three")); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testListViaObservable() { + Observable w = Observable.from(Arrays.asList("one", "two", "three")); + Observable> observable = w.toList(); @SuppressWarnings("unchecked") Observer> aObserver = mock(Observer.class); @@ -45,8 +58,8 @@ public void testList() { @Test public void testListMultipleObservers() { - Observable w = Observable.from("one", "two", "three"); - Observable> observable = Observable.create(toObservableList(w)); + Observable w = Observable.from(Arrays.asList("one", "two", "three")); + Observable> observable = w.bind(new OperatorToObservableList()); @SuppressWarnings("unchecked") Observer> o1 = mock(Observer.class); @@ -69,8 +82,8 @@ public void testListMultipleObservers() { @Test public void testListWithNullValue() { - Observable w = Observable.from("one", null, "three"); - Observable> observable = Observable.create(toObservableList(w)); + Observable w = Observable.from(Arrays.asList("one", null, "three")); + Observable> observable = w.bind(new OperatorToObservableList()); @SuppressWarnings("unchecked") Observer> aObserver = mock(Observer.class); diff --git a/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java b/rxjava-core/src/test/java/rx/operators/OperatorToObservableSortedListTest.java similarity index 86% rename from rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java rename to rxjava-core/src/test/java/rx/operators/OperatorToObservableSortedListTest.java index 2f8c31404d..3c4c5dc200 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToObservableSortedListTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorToObservableSortedListTest.java @@ -17,7 +17,6 @@ import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; -import static rx.operators.OperationToObservableSortedList.*; import java.util.Arrays; import java.util.List; @@ -29,12 +28,12 @@ import rx.Observer; import rx.util.functions.Func2; -public class OperationToObservableSortedListTest { +public class OperatorToObservableSortedListTest { @Test public void testSortedList() { Observable w = Observable.from(1, 3, 2, 5, 4); - Observable> observable = Observable.create(toSortedList(w)); + Observable> observable = w.bind(new OperatorToObservableSortedList()); @SuppressWarnings("unchecked") Observer> aObserver = mock(Observer.class); @@ -47,7 +46,7 @@ public void testSortedList() { @Test public void testSortedListWithCustomFunction() { Observable w = Observable.from(1, 3, 2, 5, 4); - Observable> observable = Observable.create(toSortedList(w, new Func2() { + Observable> observable = w.bind(new OperatorToObservableSortedList(new Func2() { @Override public Integer call(Integer t1, Integer t2) { diff --git a/settings.gradle b/settings.gradle index 22dd94ec82..2122bb8ff6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,7 +4,7 @@ include 'rxjava-core', \ 'language-adaptors:rxjava-clojure', \ 'language-adaptors:rxjava-jruby', \ 'language-adaptors:rxjava-scala', \ -'language-adaptors:rxjava-kotlin', \ +//'language-adaptors:rxjava-kotlin', \ 'rxjava-contrib:rxjava-swing', \ 'rxjava-contrib:rxjava-android', \ 'rxjava-contrib:rxjava-apache-http', \