diff --git a/build.gradle b/build.gradle index 9073b716ee..440cac0acc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,9 @@ +apply from: file('gradle/convention.gradle') +apply from: file('gradle/maven.gradle') +//apply from: file('gradle/check.gradle') +apply from: file('gradle/license.gradle') +apply from: file('gradle/release.gradle') + ext.githubProjectName = rootProject.name buildscript { @@ -9,20 +15,55 @@ allprojects { repositories { mavenCentral() } } -apply from: file('gradle/convention.gradle') -apply from: file('gradle/maven.gradle') -//apply from: file('gradle/check.gradle') -apply from: file('gradle/license.gradle') -apply from: file('gradle/release.gradle') subprojects { + apply plugin: 'java' + apply plugin: 'eclipse' + apply plugin: 'idea' group = "com.netflix.${githubProjectName}" + // make 'examples' use the same classpath + configurations { + examplesCompile.extendsFrom compile + examplesRuntime.extendsFrom runtime + } + sourceSets.test.java.srcDir 'src/main/java' tasks.withType(Javadoc).each { it.classpath = sourceSets.main.compileClasspath } + + //include /src/examples folder + sourceSets { + examples + } + + //include 'examples' in build task + tasks.build { + dependsOn(examplesClasses) + } + + eclipse { + classpath { + // include 'provided' dependencies on the classpath + plusConfigurations += configurations.provided + + downloadSources = true + downloadJavadoc = true + } + } + + idea { + module { + // include 'provided' dependencies on the classpath + scopes.PROVIDED.plus += configurations.provided + } + } +} + +project(':rxjava-core') { + sourceSets.test.java.srcDir 'src/test/java' } diff --git a/gradle.properties b/gradle.properties index bf54d765c8..fa6cdb407b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.10.2 +version=0.11.0-SNAPSHOT diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e230e2b1c4..2abe81ceda 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.3-bin.zip +distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip diff --git a/language-adaptors/rxjava-clojure/README.md b/language-adaptors/rxjava-clojure/README.md index 35faeab8ba..bb452b98a3 100644 --- a/language-adaptors/rxjava-clojure/README.md +++ b/language-adaptors/rxjava-clojure/README.md @@ -1,21 +1,57 @@ # Clojure Adaptor for RxJava +This adaptor provides functions and macros to ease Clojure/RxJava interop. In particular, there are functions and macros for turning Clojure functions and code into RxJava `Func*` and `Action*` interfaces without the tedium of manually reifying the interfaces. -This adaptor allows 'fn' functions to be used and RxJava will know how to invoke them. +# Basic Usage -This enables code such as: +## Requiring the interop namespace +The first thing to do is to require the namespace: ```clojure -(-> - (Observable/toObservable ["one" "two" "three"]) - (.take 2) - (.subscribe (fn [arg] (println arg)))) +(ns my.namespace + (:require [rx.lang.clojure.interop :as rx]) + (:import [rx Observable])) ``` -This still dependes on Clojure using Java interop against the Java API. +or, at the REPL: -A future goal is a Clojure wrapper to expose the functions in a more idiomatic way. +```clojure +(require '[rx.lang.clojure.interop :as rx]) +``` + +## Using rx/fn +Once the namespace is required, you can use the `rx/fn` macro anywhere RxJava wants a `rx.util.functions.Func` object. The syntax is exactly the same as `clojure.core/fn`: + +```clojure +(-> my-observable + (.map (rx/fn [v] (* 2 v)))) +``` + +If you already have a plain old Clojure function you'd like to use, you can pass it to the `rx/fn*` function to get a new object that implements `rx.util.functions.Func`: + +```clojure +(-> my-numbers + (.reduce (rx/fn* +))) +``` + +## Using rx/action +The `rx/action` macro is identical to `rx/fn` except that the object returned implements `rx.util.functions.Action` interfaces. It's used in `subscribe` and other side-effect-y contexts: + +```clojure +(-> my-observable + (.map (rx/fn* transform-data)) + (.finallyDo (rx/action [] (println "Finished transform"))) + (.subscribe (rx/action [v] (println "Got value" v)) + (rx/action [e] (println "Get error" e)) + (rx/action [] (println "Sequence complete")))) +``` + +# Gotchas +Here are a few things to keep in mind when using this interop: +* Keep in mind the (mostly empty) distinction between `Func` and `Action` and which is used in which contexts +* If there are multiple Java methods overloaded by `Func` arity, you'll need to use a type hint to let the compiler know which one to choose. +* Methods that take a predicate (like filter) expect the predicate to return a boolean value. A function that returns a non-boolean value will result in a `ClassCastException`. # Binaries diff --git a/language-adaptors/rxjava-clojure/build.gradle b/language-adaptors/rxjava-clojure/build.gradle index 0ea7feb27c..0aeba9680a 100644 --- a/language-adaptors/rxjava-clojure/build.gradle +++ b/language-adaptors/rxjava-clojure/build.gradle @@ -1,17 +1,12 @@ -apply plugin: 'java' apply plugin: 'clojure' -apply plugin: 'eclipse' -apply plugin: 'idea' apply plugin: 'osgi' dependencies { compile project(':rxjava-core') - provided 'org.clojure:clojure:1.4.+' - provided 'junit:junit-dep:4.10' - provided 'org.mockito:mockito-core:1.8.5' - + // clojure - testCompile 'clj-http:clj-http:0.6.4' // https://clojars.org/clj-http + compile 'org.clojure:clojure:1.4.+' + //compile 'clj-http:clj-http:0.6.4' // https://clojars.org/clj-http } /* @@ -22,11 +17,10 @@ warnOnReflection = true buildscript { repositories { maven { url "http://clojars.org/repo" } } - dependencies { classpath "clojuresque:clojuresque:1.5.4" } + dependencies { classpath "clojuresque:clojuresque:1.5.8" } } repositories { - mavenCentral() clojarsRepo() } @@ -37,45 +31,16 @@ eclipse { project { natures "ccw.nature" } - classpath { - plusConfigurations += configurations.provided - downloadSources = true - downloadJavadoc = true - } -} - -idea { - module { - // include 'provided' dependencies on the classpath - scopes.PROVIDED.plus += configurations.provided - } -} - - -eclipse { - classpath { - // include 'provided' dependencies on the classpath - plusConfigurations += configurations.provided - - downloadSources = true - downloadJavadoc = true - } } -// include /src/examples folder -sourceSets { - examples +tasks.clojureTest { + classpath = classpath + configurations.provided } -// make 'examples' use the same classpath -configurations { - examplesCompile.extendsFrom compile - examplesRuntime.extendsFrom runtime +tasks.compileExamplesClojure { + classpath = classpath + configurations.provided } -// include 'examples' in build task -build.dependsOn examplesClasses - jar { manifest { name = 'rxjava-clojure' @@ -84,4 +49,4 @@ jar { instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*' instruction 'Fragment-Host', 'com.netflix.rxjava.core' } -} \ No newline at end of file +} diff --git a/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/http_examples.txt b/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/http_examples.txt new file mode 100644 index 0000000000..c9c61f757d --- /dev/null +++ b/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/http_examples.txt @@ -0,0 +1,58 @@ +(ns rx.lang.clojure.examples.http-examples + (:require [rx.lang.clojure.interop :as rx] + [clj-http.client :as http]) + (:import rx.Observable rx.subscriptions.Subscriptions)) + +; NOTE on naming conventions. I'm using camelCase names (against clojure convention) +; in this file as I'm purposefully keeping functions and methods across +; different language implementations in-sync for easy comparison. + +(defn fetchWikipediaArticleAsynchronously [wikipediaArticleNames] + "Fetch a list of Wikipedia articles asynchronously. + + return Observable of HTML" + (Observable/create + (rx/fn [observer] + (let [f (future + (doseq [articleName wikipediaArticleNames] + (-> observer (.onNext (http/get (str "http://en.wikipedia.org/wiki/" articleName))))) + ; after sending response to onnext we complete the sequence + (-> observer .onCompleted))] + ; a subscription that cancels the future if unsubscribed + (Subscriptions/create (rx/action [] (future-cancel f))))))) + +; To see output +(comment + (-> (fetchWikipediaArticleAsynchronously ["Tiger" "Elephant"]) + (.subscribe (rx/action [v] (println "--- Article ---\n" (subs (:body v) 0 125) "..."))))) + + +; -------------------------------------------------- +; Error Handling +; -------------------------------------------------- + +(defn fetchWikipediaArticleAsynchronouslyWithErrorHandling [wikipediaArticleNames] + "Fetch a list of Wikipedia articles asynchronously + with proper error handling. + + return Observable of HTML" + (Observable/create + (rx/fn [observer] + (let [f (future + (try + (doseq [articleName wikipediaArticleNames] + (-> observer (.onNext (http/get (str "http://en.wikipedia.org/wiki/" articleName))))) + ;(catch Exception e (prn "exception"))) + (catch Exception e (-> observer (.onError e)))) + ; after sending response to onNext we complete the sequence + (-> observer .onCompleted))] + ; a subscription that cancels the future if unsubscribed + (Subscriptions/create (rx/action [] (future-cancel f))))))) + +; To see output +(comment + (-> (fetchWikipediaArticleAsynchronouslyWithErrorHandling ["Tiger" "NonExistentTitle" "Elephant"]) + (.subscribe (rx/action [v] (println "--- Article ---\n" (subs (:body v) 0 125) "...")) + (rx/action [e] (println "--- Error ---\n" (.getMessage e)))))) + + diff --git a/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/rx_examples.clj b/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/rx_examples.txt similarity index 67% rename from language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/rx_examples.clj rename to language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/rx_examples.txt index 943ecb2f17..8245840a6c 100644 --- a/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/rx_examples.clj +++ b/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/rx_examples.txt @@ -1,6 +1,6 @@ (ns rx.lang.clojure.examples.rx-examples - (:import rx.Observable rx.subscriptions.Subscriptions) - (:require [clj-http.client :as http])) + (:require [rx.lang.clojure.interop :as rx]) + (:import rx.Observable rx.subscriptions.Subscriptions)) ; NOTE on naming conventions. I'm using camelCase names (against clojure convention) ; in this file as I'm purposefully keeping functions and methods across @@ -12,8 +12,8 @@ (defn hello [& args] - (-> (Observable/toObservable args) - (.subscribe #(println (str "Hello " % "!"))))) + (-> (Observable/from args) + (.subscribe (rx/action [v] (println (str "Hello " v "!")))))) ; To see output (comment @@ -23,22 +23,13 @@ ; Create Observable from Existing Data ; -------------------------------------------------- -(defn existingDataFromNumbers [] - (Observable/toObservable [1 2 3 4 5 6])) (defn existingDataFromNumbersUsingFrom [] (Observable/from [1 2 3 4 5 6])) -(defn existingDataFromObjects [] - (Observable/toObservable ["a" "b" "c"])) - (defn existingDataFromObjectsUsingFrom [] (Observable/from ["a" "b" "c"])) -(defn existingDataFromList [] - (let [list [5, 6, 7, 8]] - (Observable/toObservable list))) - (defn existingDataFromListUsingFrom [] (let [list [5, 6, 7, 8]] (Observable/from list))) @@ -56,7 +47,7 @@ returns Observable" (Observable/create - (fn [observer] + (rx/fn [observer] (doseq [x (range 50)] (-> observer (.onNext (str "value_" x)))) ; after sending all values we complete the sequence (-> observer .onCompleted) @@ -66,7 +57,7 @@ ; To see output (comment - (.subscribe (customObservableBlocking) println)) + (.subscribe (customObservableBlocking) (rx/action* println))) (defn customObservableNonBlocking [] "This example shows a custom Observable that does not block @@ -74,38 +65,18 @@ returns Observable" (Observable/create - (fn [observer] + (rx/fn [observer] (let [f (future (doseq [x (range 50)] (-> observer (.onNext (str "anotherValue_" x)))) ; after sending all values we complete the sequence (-> observer .onCompleted))] ; return a subscription that cancels the future - (Subscriptions/create #(future-cancel f)))))) - -; To see output -(comment - (.subscribe (customObservableNonBlocking) println)) - - -(defn fetchWikipediaArticleAsynchronously [wikipediaArticleNames] - "Fetch a list of Wikipedia articles asynchronously. - - return Observable of HTML" - (Observable/create - (fn [observer] - (let [f (future - (doseq [articleName wikipediaArticleNames] - (-> observer (.onNext (http/get (str "http://en.wikipedia.org/wiki/" articleName))))) - ; after sending response to onnext we complete the sequence - (-> observer .onCompleted))] - ; a subscription that cancels the future if unsubscribed - (Subscriptions/create #(future-cancel f)))))) + (Subscriptions/create (rx/action [] (future-cancel f))))))) ; To see output (comment - (-> (fetchWikipediaArticleAsynchronously ["Tiger" "Elephant"]) - (.subscribe #(println "--- Article ---\n" (subs (:body %) 0 125) "...")))) + (.subscribe (customObservableNonBlocking) (rx/action* println))) ; -------------------------------------------------- @@ -119,8 +90,8 @@ (customObservableNonBlocking) (.skip 10) (.take 5) - (.map #(str % "_transformed")) - (.subscribe #(println "onNext =>" %)))) + (.map (rx/fn [v] (str v "_transformed"))) + (.subscribe (rx/action [v] (println "onNext =>" v))))) ; To see output (comment @@ -136,7 +107,7 @@ return Observable" (Observable/create - (fn [observer] + (rx/fn [observer] (let [f (future (try ; simulate fetching user data via network service call with latency @@ -147,14 +118,14 @@ (-> observer .onCompleted) (catch Exception e (-> observer (.onError e))))) ] ; a subscription that cancels the future if unsubscribed - (Subscriptions/create #(future-cancel f)))))) + (Subscriptions/create (rx/action [] (future-cancel f))))))) (defn getVideoBookmark [userId, videoId] "Asynchronously fetch bookmark for video return Observable" (Observable/create - (fn [observer] + (rx/fn [observer] (let [f (future (try ; simulate fetching user data via network service call with latency @@ -165,13 +136,13 @@ (-> observer .onCompleted) (catch Exception e (-> observer (.onError e)))))] ; a subscription that cancels the future if unsubscribed - (Subscriptions/create #(future-cancel f)))))) + (Subscriptions/create (rx/action [] (future-cancel f))))))) (defn getVideoMetadata [videoId, preferredLanguage] "Asynchronously fetch movie metadata for a given language return Observable" (Observable/create - (fn [observer] + (rx/fn [observer] (let [f (future (try ; simulate fetching video data via network service call with latency @@ -190,7 +161,7 @@ (-> observer .onCompleted) (catch Exception e (-> observer (.onError e))))) ] ; a subscription that cancels the future if unsubscribed - (Subscriptions/create #(future-cancel f)))))) + (Subscriptions/create (rx/action [] (future-cancel f))))))) (defn getVideoForUser [userId videoId] @@ -200,24 +171,24 @@ - user data return Observable" (let [user-observable (-> (getUser userId) - (.map (fn [user] {:user-name (:name user) + (.map (rx/fn [user] {:user-name (:name user) :language (:preferred-language user)}))) bookmark-observable (-> (getVideoBookmark userId videoId) - (.map (fn [bookmark] {:viewed-position (:position bookmark)}))) + (.map (rx/fn [bookmark] {:viewed-position (:position bookmark)}))) ; getVideoMetadata requires :language from user-observable so nest inside map function video-metadata-observable (-> user-observable (.mapMany ; fetch metadata after a response from user-observable is received - (fn [user-map] + (rx/fn [user-map] (getVideoMetadata videoId (:language user-map)))))] ; now combine 3 async sequences using zip (-> (Observable/zip bookmark-observable video-metadata-observable user-observable - (fn [bookmark-map metadata-map user-map] + (rx/fn [bookmark-map metadata-map user-map] {:bookmark-map bookmark-map :metadata-map metadata-map :user-map user-map})) ; and transform into a single response object - (.map (fn [data] + (.map (rx/fn [data] {:video-id videoId :video-metadata (:metadata-map data) :user-id userId @@ -231,37 +202,7 @@ (comment (-> (getVideoForUser 12345 78965) (.subscribe - (fn [x] (println "--- Object ---\n" x)) - (fn [e] (println "--- Error ---\n" e)) - (fn [] (println "--- Completed ---"))))) - - -; -------------------------------------------------- -; Error Handling -; -------------------------------------------------- - -(defn fetchWikipediaArticleAsynchronouslyWithErrorHandling [wikipediaArticleNames] - "Fetch a list of Wikipedia articles asynchronously - with proper error handling. - - return Observable of HTML" - (Observable/create - (fn [observer] - (let [f (future - (try - (doseq [articleName wikipediaArticleNames] - (-> observer (.onNext (http/get (str "http://en.wikipedia.org/wiki/" articleName))))) - ;(catch Exception e (prn "exception"))) - (catch Exception e (-> observer (.onError e)))) - ; after sending response to onNext we complete the sequence - (-> observer .onCompleted))] - ; a subscription that cancels the future if unsubscribed - (Subscriptions/create #(future-cancel f)))))) - -; To see output -(comment - (-> (fetchWikipediaArticleAsynchronouslyWithErrorHandling ["Tiger" "NonExistentTitle" "Elephant"]) - (.subscribe #(println "--- Article ---\n" (subs (:body %) 0 125) "...") - #(println "--- Error ---\n" (.getMessage %))))) - + (rx/action [x] (println "--- Object ---\n" x)) + (rx/action [e] (println "--- Error ---\n" e)) + (rx/action [] (println "--- Completed ---"))))) diff --git a/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/video_example.clj b/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/video_example.txt similarity index 78% rename from language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/video_example.clj rename to language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/video_example.txt index 557e54aad6..3d376be4f4 100644 --- a/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/video_example.clj +++ b/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/video_example.txt @@ -1,5 +1,7 @@ (ns rx.lang.clojure.examples.video-example - (:import [rx Observable Observer Subscription] rx.subscriptions.Subscriptions)) + (:require [rx.lang.clojure.interop :as rx]) + (:import [rx Observable Observer Subscription] + rx.subscriptions.Subscriptions)) ; Adapted from language-adaptors/rxjava-groovy/src/examples/groovy/rx/lang/groovy/examples/VideoExample.groovy @@ -21,11 +23,11 @@ ; how progressive rendering could work (println "---- sequence of video dictionaries ----") (-> (get-video-grid-for-display 1) - (.subscribe #(locking print-lock (println %)) - #(locking print-lock (println "Error: " %)) - #(do - (println "Finished example 1") - (on-complete))))) + (.subscribe (rx/action [v] (locking print-lock (println v))) + (rx/action [v] (locking print-lock (println "Error: " v))) + (rx/action [] + (println "Finished example 1") + (on-complete))))) (defn example2 [on-complete] @@ -34,9 +36,9 @@ ; for document style responses (most webservices) (-> (get-video-grid-for-display 1) .toList - (.subscribe #(println "\n ---- single list of video dictionaries ----\n" %) - #(println "Error: " %) - #(do + (.subscribe (rx/action [v] (println "\n ---- single list of video dictionaries ----\n" v)) + (rx/action [v] (println "Error: " v)) + (rx/action [] (println "Finished Example 2") (println "Exiting") (on-complete))))) @@ -61,27 +63,27 @@ " [user-id] (-> (get-list-of-lists user-id) - (.mapMany (fn [list] + (.mapMany (rx/fn [list] ; for each VideoList we want to fetch the videos (-> (video-list->videos list) (.take 10) ; we only want the first 10 of each list - (.mapMany (fn [video] + (.mapMany (rx/fn [video] ; for each video we want to fetch metadata (let [m (-> (video->metadata video) - (.map (fn [md] + (.map (rx/fn [md] ; transform to the data and format we want {:title (:title md) :length (:duration md) }))) b (-> (video->bookmark video user-id) - (.map (fn [position] + (.map (rx/fn [position] {:bookmark position}))) r (-> (video->rating video user-id) - (.map (fn [rating] + (.map (rx/fn [rating] {:rating {:actual (:actual-star-rating rating) :average (:average-star-rating rating) :predicted (:predicted-star-rating rating) }})))] ; join these together into a single, merged map for each video - (Observable/zip m b r (fn [m b r] + (Observable/zip m b r (rx/fn [m b r] (merge {:id video} m b r))))))))))) @@ -91,9 +93,10 @@ "Returns an observable that executes (f observer) in a future, returning a subscription that will cancel the future." [f] - (Observable/create (fn [^Observer observer] + (Observable/create (rx/fn [^Observer observer] + (println "Starting f") (let [f (future (f observer))] - (Subscriptions/create #(future-cancel f)))))) + (Subscriptions/create (rx/action [] (future-cancel f))))))) (defn ^Observable get-list-of-lists " @@ -109,7 +112,8 @@ (.onCompleted observer)))) -(comment (.subscribe (get-list-of-lists 7777) println)) +(comment (.subscribe (get-list-of-lists 7777) + (rx/action* println))) (defn video-list [position] @@ -118,7 +122,7 @@ (defn ^Observable video-list->videos [{:keys [position] :as video-list}] - (Observable/create (fn [^Observer observer] + (Observable/create (rx/fn [^Observer observer] (dotimes [i 50] (.onNext observer (+ (* position 1000) i))) (.onCompleted observer) @@ -128,23 +132,25 @@ (defn ^Observable video->metadata [video-id] - (Observable/create (fn [^Observer observer] + (Observable/create (rx/fn [^Observer observer] (.onNext observer {:title (str "video-" video-id "-title") :actors ["actor1" "actor2"] :duration 5428 }) (.onCompleted observer) (Subscriptions/empty)))) -(comment (.subscribe (video->metadata 10) println)) +(comment (.subscribe (video->metadata 10) (rx/action* println))) (defn ^Observable video->bookmark [video-id user-id] (future-observable (fn [^Observer observer] (Thread/sleep 4) + (println "onNext") (.onNext observer (if (> (rand-int 6) 1) 0 (rand-int 4000))) + (println "onComplete") (.onCompleted observer)))) -(comment (.subscribe (video->bookmark 112345 99999) println)) +(comment (.subscribe (video->bookmark 112345 99999) (rx/action* println))) (defn ^Observable video->rating [video-id user-id] diff --git a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/interop.clj b/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/interop.clj new file mode 100644 index 0000000000..de87e384dc --- /dev/null +++ b/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/interop.clj @@ -0,0 +1,98 @@ +(ns rx.lang.clojure.interop + "Functions an macros for instantiating rx Func* and Action* interfaces." + (:refer-clojure :exclude [fn])) + +(def ^:private -ns- *ns*) +(set! *warn-on-reflection* true) + +(defmacro ^:private reify-callable + "Reify a bunch of Callable-like interfaces + + prefix fully qualified interface name. numbers will be appended + arities vector of desired arities + f the function to execute + + " + [prefix arities f] + (let [f-name (gensym "rc")] + `(let [~f-name ~f] + (reify + ~@(mapcat (clojure.core/fn [n] + (let [ifc-sym (symbol (str prefix n)) + arg-syms (map #(symbol (str "v" %)) (range n))] + `(~ifc-sym + (~'call ~(vec (cons 'this arg-syms)) + ~(cons f-name arg-syms))))) + arities) )))) + +(defn fn* + "Given function f, returns an object that implements rx.util.functions.Func0-9 + by delegating the call() method to the given function. + + If the f has the wrong arity, an ArityException will be thrown at runtime. + + Example: + + (.reduce my-numbers (rx/fn* +)) + + See: + http://netflix.github.io/RxJava/javadoc/rx/util/functions/Func0.html + " + [f] + (reify-callable "rx.util.functions.Func" [0 1 2 3 4 5 6 7 8 9] f)) + +(defn fnN* + "Given function f, returns an object that implements rx.util.functions.FuncN + by delegating to the given function. + + Unfortunately, this can't be included in fn* because of ambiguities between + the single arg call() method and the var args call method. + + See: + http://netflix.github.io/RxJava/javadoc/rx/util/functions/FuncN.html + " + [f] + (reify rx.util.functions.FuncN + (call [this objects] + (apply f objects)))) + +(defmacro fn + "Like clojure.core/fn, but returns the appropriate rx.util.functions.Func* + interface. + + Example: + + (.map my-observable (rx/fn [a] (* 2 a))) + + " + [& fn-form] + ; have to qualify fn*. Otherwise bad things happen with the fn* special form in clojure + `(rx.lang.clojure.interop/fn* (clojure.core/fn ~@fn-form))) + +(defn action* + "Given function f, returns an object that implements rx.util.functions.Action0-9 + by delegating to the given function. + + Example: + + (.subscribe my-observable (rx/action* println)) + + See: + http://netflix.github.io/RxJava/javadoc/rx/util/functions/Action0.html + " + [f] + (reify-callable "rx.util.functions.Action" [0 1 2 3] f)) + +(defmacro action + "Like clojure.core/fn, but returns the appropriate rx.util.functions.Action* + interface. + + Example: + + (.finallyDo my-observable (rx/action [] (println \"Finally!\"))) + + " + [& fn-form] + `(action* (clojure.core/fn ~@fn-form))) + +;################################################################################ diff --git a/language-adaptors/rxjava-clojure/src/main/java/rx/lang/clojure/ClojureAdaptor.java b/language-adaptors/rxjava-clojure/src/main/java/rx/lang/clojure/ClojureAdaptor.java deleted file mode 100644 index 4420f7c919..0000000000 --- a/language-adaptors/rxjava-clojure/src/main/java/rx/lang/clojure/ClojureAdaptor.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * 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.clojure; - -import java.util.Arrays; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import rx.Observer; -import rx.util.functions.FunctionLanguageAdaptor; - -import clojure.lang.IFn; -import clojure.lang.RT; -import clojure.lang.Var; - -public class ClojureAdaptor implements FunctionLanguageAdaptor { - - @Override - public Object call(Object function, Object[] args) { - if (args.length == 0) { - return ((IFn) function).invoke(); - } else if (args.length == 1) { - return ((IFn) function).invoke(args[0]); - } else if (args.length == 2) { - return ((IFn) function).invoke(args[0], args[1]); - } else if (args.length == 3) { - return ((IFn) function).invoke(args[0], args[1], args[2]); - } else if (args.length == 4) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3]); - } else if (args.length == 5) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4]); - } else if (args.length == 6) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5]); - } else if (args.length == 7) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); - } else if (args.length == 8) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); - } else if (args.length == 9) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); - } else if (args.length == 10) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); - } else if (args.length == 11) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]); - } else if (args.length == 12) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11]); - } else if (args.length == 13) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12]); - } else if (args.length == 14) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13]); - } else if (args.length == 15) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14]); - } else if (args.length == 16) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15]); - } else if (args.length == 17) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16]); - } else if (args.length == 18) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17]); - } else if (args.length == 19) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18]); - } else if (args.length == 20) { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18], args[19]); - } else { - return ((IFn) function).invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18], args[19], Arrays.copyOfRange(args, 20, args.length)); - } - } - - @Override - public Class[] getFunctionClass() { - return new Class[] { IFn.class }; - } - - public static class UnitTest { - - @Mock - ScriptAssertion assertion; - - @Mock - Observer w; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testTake() { - runClojureScript("(-> (rx.Observable/toObservable [\"one\" \"two\" \"three\"]) (.take 2) (.subscribe (fn [arg] (println arg))))"); - } - - // commented out for now as I can't figure out how to set the var 'a' with the 'assertion' instance when running the code from java - // @Test - // public void testFilter() { - // runClojureScript("(-> (org.rx.reactive.Observable/toObservable [1 2 3]) (.filter (fn [v] (>= v 2))) (.subscribe (fn [result] (a.received(result)))))"); - // verify(assertion, times(0)).received(1); - // verify(assertion, times(1)).received(2); - // verify(assertion, times(1)).received(3); - // } - - private static interface ScriptAssertion { - public void error(Exception o); - - public void received(Object o); - } - - private void runClojureScript(String script) { - Object code = RT.var("clojure.core", "read-string").invoke(script); - Var eval = RT.var("clojure.core", "eval"); - Object result = eval.invoke(code); - System.out.println("Result: " + result); - } - } -} diff --git a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/interop_test.clj b/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/interop_test.clj new file mode 100644 index 0000000000..72dcadaa6e --- /dev/null +++ b/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/interop_test.clj @@ -0,0 +1,88 @@ +(ns rx.lang.clojure.interop-test + (:require [rx.lang.clojure.interop :as rx] + [clojure.test :refer [deftest testing is]]) + (:import [rx Observable] + [rx.observables BlockingObservable] + )) + +(deftest test-fn* + (testing "implements Func0-9" + (let [f (rx/fn* vector)] + (is (instance? rx.util.functions.Func0 f)) + (is (instance? rx.util.functions.Func1 f)) + (is (instance? rx.util.functions.Func2 f)) + (is (instance? rx.util.functions.Func3 f)) + (is (instance? rx.util.functions.Func4 f)) + (is (instance? rx.util.functions.Func5 f)) + (is (instance? rx.util.functions.Func6 f)) + (is (instance? rx.util.functions.Func7 f)) + (is (instance? rx.util.functions.Func8 f)) + (is (instance? rx.util.functions.Func9 f)) + (is (= [] (.call f))) + (is (= [1] (.call f 1))) + (is (= [1 2] (.call f 1 2))) + (is (= [1 2 3] (.call f 1 2 3))) + (is (= [1 2 3 4] (.call f 1 2 3 4))) + (is (= [1 2 3 4 5] (.call f 1 2 3 4 5))) + (is (= [1 2 3 4 5 6] (.call f 1 2 3 4 5 6))) + (is (= [1 2 3 4 5 6 7] (.call f 1 2 3 4 5 6 7))) + (is (= [1 2 3 4 5 6 7 8] (.call f 1 2 3 4 5 6 7 8))) + (is (= [1 2 3 4 5 6 7 8 9] (.call f 1 2 3 4 5 6 7 8 9)))))) + +(deftest test-fn + (testing "makes appropriate Func*" + (let [f (rx/fn [a b c] (println "test-fn") (+ a b c))] + (is (= 6 (.call f 1 2 3)))))) + +(deftest test-fnN* + (testing "implements FuncN" + (is (= (vec (range 99)) + (.call (rx/fnN* vector) (into-array Object (range 99))))))) + +(deftest test-action* + (testing "implements Action0-3" + (let [calls (atom []) + a (rx/action* #(swap! calls conj (vec %&)))] + (is (instance? rx.util.functions.Action0 a)) + (is (instance? rx.util.functions.Action1 a)) + (is (instance? rx.util.functions.Action2 a)) + (is (instance? rx.util.functions.Action3 a)) + (.call a) + (.call a 1) + (.call a 1 2) + (.call a 1 2 3) + (is (= [[] [1] [1 2] [1 2 3]]))))) + +(deftest test-action + (testing "makes appropriate Action*" + (let [called (atom nil) + a (rx/action [a b] (reset! called [a b]))] + (.call a 9 10) + (is (= [9 10] @called))))) + +(deftest test-basic-usage + + (testing "can pass rx/fn to map and friends" + (is (= (+ 1 4 9) + (-> (Observable/from [1 2 3]) + (.map (rx/fn [v] (* v v))) + (.reduce (rx/fn* +)) + (BlockingObservable/single))))) + + (testing "can pass rx/action to subscribe and friends" + (let [finally-called (atom nil) + complete-called (promise) + result (atom []) + o (-> (Observable/from ["4" "5" "6"]) + (.map (rx/fn* #(Long/parseLong %))) + (.finallyDo (rx/action [] + (reset! finally-called true))) + (.reduce (rx/fn [a v] (* a v))) + (.subscribe (rx/action [v] (swap! result conj v)) + (rx/action [e]) + (rx/action [] (deliver complete-called true)))) ] + (is (= true @complete-called)) + (is (= true @finally-called)) + (is (= [(* 4 5 6)] @result))))) + +;################################################################################ diff --git a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/observable_tests.clj b/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/observable_tests.clj deleted file mode 100644 index f0521c2127..0000000000 --- a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/observable_tests.clj +++ /dev/null @@ -1,7 +0,0 @@ -(ns rx.lang.clojure.observable-tests - (import rx.Observable)) - -;; still need to get this wired up in build.gradle to run as tests -; (-> (rx.Observable/toObservable ["one" "two" "three"]) (.take 2) (.subscribe (fn [arg] (println arg)))) - -; (-> (rx.Observable/toObservable [1 2 3]) (.takeWhile (fn [x i] (< x 2))) (.subscribe (fn [arg] (println arg)))) diff --git a/language-adaptors/rxjava-groovy/build.gradle b/language-adaptors/rxjava-groovy/build.gradle index 91c060193d..c26a383a34 100644 --- a/language-adaptors/rxjava-groovy/build.gradle +++ b/language-adaptors/rxjava-groovy/build.gradle @@ -1,47 +1,13 @@ -apply plugin: 'java' apply plugin: 'groovy' -apply plugin: 'eclipse' -apply plugin: 'idea' apply plugin: 'osgi' dependencies { compile project(':rxjava-core') - groovy 'org.codehaus.groovy:groovy-all:2.+' + compile 'org.codehaus.groovy:groovy-all:2.+' provided 'junit:junit-dep:4.10' provided 'org.mockito:mockito-core:1.8.5' } -// include /src/examples folder -sourceSets { - examples -} - -// make 'examples' use the same classpath -configurations { - examplesCompile.extendsFrom compile - examplesRuntime.extendsFrom runtime -} - -// include 'examples' in build task -build.dependsOn examplesClasses - -eclipse { - classpath { - // include 'provided' dependencies on the classpath - plusConfigurations += configurations.provided - - downloadSources = true - downloadJavadoc = true - } -} - -idea { - module { - // include 'provided' dependencies on the classpath - scopes.PROVIDED.plus += configurations.provided - } -} - jar { manifest { name = 'rxjava-groovy' diff --git a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyActionWrapper.java b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyActionWrapper.java new file mode 100644 index 0000000000..24394e1db7 --- /dev/null +++ b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyActionWrapper.java @@ -0,0 +1,61 @@ +/** + * 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.util.functions.Action; +import rx.util.functions.Action0; +import rx.util.functions.Action1; +import rx.util.functions.Action2; +import rx.util.functions.Action3; + +/** + * Concrete wrapper that accepts a {@link Closure} and produces any needed Rx {@link Action}. + * + * @param + * @param + * @param + * @param + */ +public class GroovyActionWrapper implements Action, Action0, Action1, Action2, Action3 { + + private final Closure closure; + + public GroovyActionWrapper(Closure closure) { + this.closure = closure; + } + + @Override + public void call() { + closure.call(); + } + + @Override + public void call(T1 t1) { + closure.call(t1); + } + + @Override + public void call(T1 t1, T2 t2) { + closure.call(t1, t2); + } + + @Override + public void call(T1 t1, T2 t2, T3 t3) { + closure.call(t1, t2, t3); + } + +} \ No newline at end of file diff --git a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyFunctionWrapper.java b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyFunctionWrapper.java new file mode 100644 index 0000000000..6d4d34ef8e --- /dev/null +++ b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyFunctionWrapper.java @@ -0,0 +1,68 @@ +/** + * 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.util.functions.Func0; +import rx.util.functions.Func1; +import rx.util.functions.Func2; +import rx.util.functions.Func3; +import rx.util.functions.Func4; +import rx.util.functions.Function; + +/** + * Concrete wrapper that accepts a {@link Closure} and produces any needed Rx {@link Function}. + * + * @param + * @param + * @param + * @param + * @param + */ +public class GroovyFunctionWrapper implements Func0, Func1, Func2, Func3, Func4 { + + private final Closure closure; + + + public GroovyFunctionWrapper(Closure closure) { + this.closure = closure; + } + + @Override + public R call() { + return (R) closure.call(); + } + + @Override + public R call(T1 t1) { + return (R) closure.call(t1); + } + + @Override + public R call(T1 t1, T2 t2) { + return (R) closure.call(t1, t2); + } + + @Override + public R call(T1 t1, T2 t2, T3 t3) { + return (R) closure.call(t1, t2, t3); + } + + @Override + public R call(T1 t1, T2 t2, T3 t3, T4 t4) { + return (R) closure.call(t1, t2, t3, t4); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..91350cb56c --- /dev/null +++ b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyExtensionModule.java @@ -0,0 +1,179 @@ +/** + * 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 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.observables.BlockingObservable; +import rx.util.functions.Action; +import rx.util.functions.Function; + +/** + * ExtensionModule that adds extension methods to support groovy.lang.Closure + * anywhere rx.util.functions.Function/Action is used in classes defined in CLASS_TO_EXTEND. + * + * It is specifically intended for providing extension methods on Observable. + */ +public class RxGroovyExtensionModule extends ExtensionModule { + + @SuppressWarnings("rawtypes") + private final static Class[] CLASS_TO_EXTEND = new Class[] { Observable.class, BlockingObservable.class }; + + public RxGroovyExtensionModule() { + super("RxGroovyExtensionModule", "1.0"); + } + + /** + * Keeping this code around a little while as it was hard to figure out ... and I'm still messing with it while debugging. + * + * Once the rest of this ExtensionModule stuff is working I'll delete this method. + * + * This is used for manually initializing rather than going via the org.codehaus.groovy.runtime.ExtensionModule properties file. + */ + public static void initializeManuallyForTesting() { + System.out.println("initialize"); + MetaClassRegistryImpl mcRegistry = ((MetaClassRegistryImpl) GroovySystem.getMetaClassRegistry()); + // RxGroovyExtensionModule em = new RxGroovyExtensionModule(); + + Properties p = new Properties(); + p.setProperty("moduleFactory", "rx.lang.groovy.RxGroovyPropertiesModuleFactory"); + Map> metaMethods = new HashMap>(); + mcRegistry.registerExtensionModuleFromProperties(p, RxGroovyExtensionModule.class.getClassLoader(), metaMethods); + + for (ExtensionModule m : mcRegistry.getModuleRegistry().getModules()) { + System.out.println("Module: " + m.getName()); + } + + for (CachedClass cc : metaMethods.keySet()) { + System.out.println("Adding MetaMethods to CachedClass: " + cc); + cc.addNewMopMethods(metaMethods.get(cc)); + } + } + + @SuppressWarnings("rawtypes") + @Override + public List getMetaMethods() { + // System.out.println("**** RxGroovyExtensionModule => Initializing and returning MetaMethods."); + List methods = new ArrayList(); + + for (Class classToExtend : CLASS_TO_EXTEND) { + for (final Method m : classToExtend.getMethods()) { + for (Class c : m.getParameterTypes()) { + if (Function.class.isAssignableFrom(c)) { + methods.add(createMetaMethod(m)); + // break out of parameter-type loop + break; + } + } + } + } + + return methods; + } + + private MetaMethod createMetaMethod(final Method m) { + return new MetaMethod() { + + @Override + public int getModifiers() { + return m.getModifiers(); + } + + @Override + public String getName() { + return m.getName(); + } + + @SuppressWarnings("rawtypes") + @Override + public Class getReturnType() { + return m.getReturnType(); + } + + @Override + public CachedClass getDeclaringClass() { + return ReflectionCache.getCachedClass(m.getDeclaringClass()); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public Object invoke(Object object, Object[] arguments) { + // System.out.println("***** RxGroovyExtensionModule => invoked [" + getName() + "]: " + object + " args: " + arguments[0]); + try { + Object[] newArgs = new Object[arguments.length]; + for (int i = 0; i < arguments.length; i++) { + final Object o = arguments[i]; + if (o instanceof Closure) { + if (Action.class.isAssignableFrom(m.getParameterTypes()[i])) { + newArgs[i] = new GroovyActionWrapper((Closure) o); + } else { + newArgs[i] = new GroovyFunctionWrapper((Closure) o); + } + + } else { + newArgs[i] = o; + } + } + return m.invoke(object, newArgs); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + // re-throw whatever was thrown to us + throw (RuntimeException) e.getCause(); + } else { + throw new RuntimeException(e); + } + } + } + + @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-groovy/src/main/java/rx/lang/groovy/GroovyAdaptor.java b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyPropertiesModuleFactory.java similarity index 50% rename from language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyAdaptor.java rename to language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyPropertiesModuleFactory.java index 70cef9c18e..149bca5621 100644 --- a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyAdaptor.java +++ b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyPropertiesModuleFactory.java @@ -15,17 +15,23 @@ */ package rx.lang.groovy; -import groovy.lang.Closure; -import rx.util.functions.FunctionLanguageAdaptor; +import java.util.Properties; -public class GroovyAdaptor implements FunctionLanguageAdaptor { +import org.codehaus.groovy.runtime.m12n.ExtensionModule; +import org.codehaus.groovy.runtime.m12n.PropertiesModuleFactory; + +/** + * Factory for {@link RxGroovyExtensionModule} to add extension methods. + *

+ * This is loaded from /META-INF/services/org.codehaus.groovy.runtime.ExtensionModule + *

+ * The property is defined as: moduleFactory=rx.lang.groovy.RxGroovyPropertiesModuleFactory + */ +public class RxGroovyPropertiesModuleFactory extends PropertiesModuleFactory { @Override - public Object call(Object function, Object[] args) { - return ((Closure) function).call(args); + public ExtensionModule newModule(Properties properties, ClassLoader classLoader) { + return new RxGroovyExtensionModule(); } - public Class[] getFunctionClass() { - return new Class[] { Closure.class }; - } } diff --git a/language-adaptors/rxjava-groovy/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule b/language-adaptors/rxjava-groovy/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule new file mode 100644 index 0000000000..975068e80c --- /dev/null +++ b/language-adaptors/rxjava-groovy/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule @@ -0,0 +1 @@ +moduleFactory=rx.lang.groovy.RxGroovyPropertiesModuleFactory \ No newline at end of file diff --git a/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/ObservableTests.groovy b/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/ObservableTests.groovy index dd36af1088..e0fdfdcfc3 100644 --- a/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/ObservableTests.groovy +++ b/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/ObservableTests.groovy @@ -58,7 +58,7 @@ def class ObservableTests { @Test public void testFilter() { - Observable.filter(Observable.from(1, 2, 3), {it >= 2}).subscribe({ result -> a.received(result)}); + Observable.from(1, 2, 3).filter({it >= 2}).subscribe({ result -> a.received(result)}); verify(a, times(0)).received(1); verify(a, times(1)).received(2); verify(a, times(1)).received(3); @@ -82,7 +82,7 @@ def class ObservableTests { @Test public void testMap2() { - Observable.map(Observable.from(1, 2, 3), {'hello_' + it}).subscribe({ result -> a.received(result)}); + Observable.from(1, 2, 3).map({'hello_' + it}).subscribe({ result -> a.received(result)}); verify(a, times(1)).received("hello_" + 1); verify(a, times(1)).received("hello_" + 2); verify(a, times(1)).received("hello_" + 3); @@ -90,7 +90,7 @@ def class ObservableTests { @Test public void testMaterialize() { - Observable.materialize(Observable.from(1, 2, 3)).subscribe({ result -> a.received(result)}); + Observable.from(1, 2, 3).materialize().subscribe({ result -> a.received(result)}); // we expect 4 onNext calls: 3 for 1, 2, 3 ObservableNotification.OnNext and 1 for ObservableNotification.OnCompleted verify(a, times(4)).received(any(Notification.class)); verify(a, times(0)).error(any(Exception.class)); @@ -162,7 +162,7 @@ def class ObservableTests { @Test public void testSkipTake() { - Observable.skip(Observable.from(1, 2, 3), 1).take(1).subscribe({ result -> a.received(result)}); + Observable.from(1, 2, 3).skip(1).take(1).subscribe({ result -> a.received(result)}); verify(a, times(0)).received(1); verify(a, times(1)).received(2); verify(a, times(0)).received(3); @@ -170,7 +170,7 @@ def class ObservableTests { @Test public void testSkip() { - Observable.skip(Observable.from(1, 2, 3), 2).subscribe({ result -> a.received(result)}); + Observable.from(1, 2, 3).skip(2).subscribe({ result -> a.received(result)}); verify(a, times(0)).received(1); verify(a, times(0)).received(2); verify(a, times(1)).received(3); @@ -178,7 +178,7 @@ def class ObservableTests { @Test public void testTake() { - Observable.take(Observable.from(1, 2, 3), 2).subscribe({ result -> a.received(result)}); + Observable.from(1, 2, 3).take(2).subscribe({ result -> a.received(result)}); verify(a, times(1)).received(1); verify(a, times(1)).received(2); verify(a, times(0)).received(3); @@ -192,7 +192,7 @@ def class ObservableTests { @Test public void testTakeWhileViaGroovy() { - Observable.takeWhile(Observable.from(1, 2, 3), { x -> x < 3}).subscribe({ result -> a.received(result)}); + Observable.from(1, 2, 3).takeWhile( { x -> x < 3}).subscribe({ result -> a.received(result)}); verify(a, times(1)).received(1); verify(a, times(1)).received(2); verify(a, times(0)).received(3); @@ -200,7 +200,7 @@ def class ObservableTests { @Test public void testTakeWhileWithIndexViaGroovy() { - Observable.takeWhileWithIndex(Observable.from(1, 2, 3), { x, i -> i < 2}).subscribe({ result -> a.received(result)}); + Observable.from(1, 2, 3).takeWhileWithIndex({ x, i -> i < 2}).subscribe({ result -> a.received(result)}); verify(a, times(1)).received(1); verify(a, times(1)).received(2); verify(a, times(0)).received(3); @@ -212,24 +212,12 @@ def class ObservableTests { verify(a, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); } - @Test - public void testToSortedListStatic() { - Observable.toSortedList(Observable.from(1, 3, 2, 5, 4)).subscribe({ result -> a.received(result)}); - verify(a, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); - } - @Test public void testToSortedListWithFunction() { new TestFactory().getNumbers().toSortedList({a, b -> a - b}).subscribe({ result -> a.received(result)}); verify(a, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); } - @Test - public void testToSortedListWithFunctionStatic() { - Observable.toSortedList(Observable.from(1, 3, 2, 5, 4), {a, b -> a - b}).subscribe({ result -> a.received(result)}); - verify(a, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); - } - @Test public void testForEach() { Observable.create(new AsyncObservable()).toBlockingObservable().forEach({ result -> a.received(result)}); diff --git a/language-adaptors/rxjava-jruby/README.md b/language-adaptors/rxjava-jruby/README.md deleted file mode 100644 index 9aec635da3..0000000000 --- a/language-adaptors/rxjava-jruby/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# JRuby Adaptor for RxJava - - -This adaptor allows `org.jruby.RubyProc` lambda functions to be used and RxJava will know how to invoke them. - -This enables code such as: - -```ruby - Observable.from("one", "two", "three") - .take(2) - .subscribe(lambda { |arg| puts arg }) -``` - -# Binaries - -Binaries and dependency information for Maven, Ivy, Gradle and others can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22rxjava-jruby%22). - -Example for Maven: - -```xml - - com.netflix.rxjava - rxjava-jruby - x.y.z - -``` - -and for Ivy: - -```xml - -``` - -and for JBundler: - -```ruby -jar 'com.netflix.rxjava:rxjava-ruby', 'x.y.z' -``` diff --git a/language-adaptors/rxjava-jruby/build.gradle b/language-adaptors/rxjava-jruby/build.gradle deleted file mode 100644 index cf7d9533e2..0000000000 --- a/language-adaptors/rxjava-jruby/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' -apply plugin: 'osgi' - -dependencies { - compile project(':rxjava-core') - provided 'org.jruby:jruby:1.6+' - provided 'junit:junit-dep:4.10' - provided 'org.mockito:mockito-core:1.8.5' -} - -eclipse { - classpath { - // include 'provided' dependencies on the classpath - plusConfigurations += configurations.provided - - downloadSources = true - downloadJavadoc = true - } -} - -idea { - module { - // include 'provided' dependencies on the classpath - scopes.PROVIDED.plus += configurations.provided - } -} - -jar { - manifest { - name = 'rxjava-jruby' - instruction 'Bundle-Vendor', 'Netflix' - instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava' - instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*' - instruction 'Fragment-Host', 'com.netflix.rxjava.core' - } -} \ No newline at end of file diff --git a/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyAdaptor.java b/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyAdaptor.java deleted file mode 100644 index d66dc16acf..0000000000 --- a/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyAdaptor.java +++ /dev/null @@ -1,225 +0,0 @@ -/** - * 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.jruby; - -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; - -import org.jruby.Ruby; -import org.jruby.RubyProc; -import org.jruby.embed.ScriptingContainer; -import org.jruby.javasupport.JavaEmbedUtils; -import org.jruby.runtime.builtin.IRubyObject; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import rx.Notification; -import rx.Observable; -import rx.Observer; -import rx.Subscription; -import rx.util.functions.Func1; -import rx.util.functions.FunctionLanguageAdaptor; - -public class JRubyAdaptor implements FunctionLanguageAdaptor { - - @Override - public Object call(Object function, Object[] args) { - RubyProc rubyProc = ((RubyProc) function); - Ruby ruby = rubyProc.getRuntime(); - IRubyObject rubyArgs[] = new IRubyObject[args.length]; - for (int i = 0; i < args.length; i++) { - rubyArgs[i] = JavaEmbedUtils.javaToRuby(ruby, args[i]); - } - return rubyProc.getBlock().call(ruby.getCurrentContext(), rubyArgs); - } - - @Override - public Class[] getFunctionClass() { - return new Class[] { RubyProc.class }; - } - - public static class UnitTest { - - @Mock - ScriptAssertion assertion; - - @Mock - Observer w; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testCreateViaGroovy() { - runGroovyScript("Observable.create(lambda{|it| it.onNext('hello');it.onCompleted();}).subscribe(lambda{|result| a.received(result)});"); - verify(assertion, times(1)).received("hello"); - } - - @Test - public void testFilterViaGroovy() { - runGroovyScript("Observable.filter(Observable.from(1, 2, 3), lambda{|it| it >= 2}).subscribe(lambda{|result| a.received(result)});"); - verify(assertion, times(0)).received(1L); - verify(assertion, times(1)).received(2L); - verify(assertion, times(1)).received(3L); - } - - @Test - public void testLast() { - String script = "mockApiCall.getObservable().takeLast(1).subscribe(lambda{|result| a.received(result)})"; - runGroovyScript(script); - verify(assertion, times(1)).received("hello_1"); - } - - @Test - public void testMap() { - String script = "mockApiCall.getObservable().map(lambda{|v| 'say' + v}).subscribe(lambda{|result| a.received(result)});"; - runGroovyScript(script); - verify(assertion, times(1)).received("sayhello_1"); - } - - @Test - public void testMaterializeViaGroovy() { - runGroovyScript("Observable.materialize(Observable.from(1, 2, 3)).subscribe(lambda{|result| a.received(result)});"); - // we expect 4 onNext calls: 3 for 1, 2, 3 ObservableNotification.OnNext and 1 for ObservableNotification.OnCompleted - verify(assertion, times(4)).received(any(Notification.class)); - verify(assertion, times(0)).error(any(Exception.class)); - } - - @Test - public void testScriptWithMaterialize() { - String script = "mockApiCall.getObservable().materialize().subscribe(lambda{|result| a.received(result)});"; - runGroovyScript(script); - // 2 times: once for hello_1 and once for onCompleted - verify(assertion, times(2)).received(any(Notification.class)); - } - - @Test - public void testScriptWithMerge() { - String script = "Observable.merge(mockApiCall.getObservable(), mockApiCall.getObservable()).subscribe(lambda{|result| a.received(result)});"; - runGroovyScript(script); - verify(assertion, times(1)).received("hello_1"); - verify(assertion, times(1)).received("hello_2"); - } - - @Test - public void testScriptWithOnNext() { - String script = "mockApiCall.getObservable().subscribe(lambda{|result| a.received(result)})"; - runGroovyScript(script); - verify(assertion).received("hello_1"); - } - - @Test - public void testSkipTakeViaGroovy() { - runGroovyScript("Observable.skip(Observable.from(1, 2, 3), 1).take(1).subscribe(lambda{|result| a.received(result)});"); - verify(assertion, times(0)).received(1); - verify(assertion, times(1)).received(2L); - verify(assertion, times(0)).received(3); - } - - @Test - public void testSkipViaGroovy() { - runGroovyScript("Observable.skip(Observable.from(1, 2, 3), 2).subscribe(lambda{|result| a.received(result)});"); - verify(assertion, times(0)).received(1); - verify(assertion, times(0)).received(2); - verify(assertion, times(1)).received(3L); - } - - @Test - public void testTakeViaGroovy() { - runGroovyScript("Observable.take(Observable.from(1, 2, 3), 2).subscribe(lambda{|result| a.received(result)});"); - verify(assertion, times(1)).received(1L); - verify(assertion, times(1)).received(2L); - verify(assertion, times(0)).received(3); - } - - @Test - public void testToSortedList() { - runGroovyScript("mockApiCall.getNumbers().toSortedList().subscribe(lambda{|result| a.received(result)});"); - verify(assertion, times(1)).received(Arrays.asList(1, 2, 3, 4, 5)); - } - - private void runGroovyScript(String script) { - ScriptingContainer container = new ScriptingContainer(); - container.put("mockApiCall", new TestFactory()); - container.put("a", assertion); - - StringBuilder b = new StringBuilder(); - // force JRuby to always use subscribe(Object) - b.append("import \"rx.Observable\"").append("\n"); - b.append("class Observable").append("\n"); - b.append(" java_alias :subscribe, :subscribe, [java.lang.Object]").append("\n"); - b.append("end").append("\n"); - b.append(script); - - container.runScriptlet(b.toString()); - } - - private static interface ScriptAssertion { - public void error(Exception o); - - public void received(Object o); - } - - public static class TestFactory { - int counter = 1; - - public Observable getNumbers() { - return Observable.from(1, 3, 2, 5, 4); - } - - public TestObservable getObservable() { - return new TestObservable(counter++); - } - } - - private static class TestObservable extends Observable { - private final int count; - - public TestObservable(int count) { - super(new Func1, Subscription>() { - - @Override - public Subscription call(Observer t1) { - // do nothing, override subscribe for test - return null; - } - }); - this.count = count; - } - - public Subscription subscribe(Observer observer) { - - observer.onNext("hello_" + count); - observer.onCompleted(); - - return new Subscription() { - - public void unsubscribe() { - // unregister ... will never be called here since we are executing synchronously - } - - }; - } - } - } - -} diff --git a/language-adaptors/rxjava-scala/build.gradle b/language-adaptors/rxjava-scala/build.gradle index 43704248d6..576158a64c 100644 --- a/language-adaptors/rxjava-scala/build.gradle +++ b/language-adaptors/rxjava-scala/build.gradle @@ -1,11 +1,10 @@ apply plugin: 'scala' -apply plugin: 'eclipse' -apply plugin: 'idea' apply plugin: 'osgi' tasks.withType(ScalaCompile) { scalaCompileOptions.fork = true scalaCompileOptions.unchecked = true + scalaCompileOptions.setAdditionalParameters(['-feature']) configure(scalaCompileOptions.forkOptions) { memoryMaximumSize = '1g' @@ -13,46 +12,38 @@ tasks.withType(ScalaCompile) { } } +sourceSets { + test { + scala { + srcDir 'src/main/scala' + } + } +} + dependencies { - // Scala compiler and related tools - scalaTools 'org.scala-lang:scala-compiler:2.10+' - scalaTools 'org.scala-lang:scala-library:2.10+' - provided 'org.scalatest:scalatest_2.10:1.9.1' + compile 'org.scala-lang:scala-library:2.10+' compile project(':rxjava-core') + provided 'junit:junit-dep:4.10' provided 'org.mockito:mockito-core:1.8.5' + provided 'org.scalatest:scalatest_2.10:1.9.1' +} - testCompile 'org.scalatest:scalatest_2.10:1.9.1' +tasks.compileScala { + classpath = classpath + (configurations.compile + configurations.provided) } task test(overwrite: true, dependsOn: testClasses) << { ant.taskdef(name: 'scalatest', classname: 'org.scalatest.tools.ScalaTestAntTask', - classpath: sourceSets.test.runtimeClasspath.asPath + classpath: configurations.provided.asPath + ':' + configurations.testRuntime.asPath + ":" + compileScala.destinationDir ) - ant.scalatest(runpath: sourceSets.test.classesDir, + ant.scalatest(runpath: sourceSets.test.output.classesDir, haltonfailure: 'true', fork: 'false') {reporter(type: 'stdout')} } -eclipse { - classpath { - // include 'provided' dependencies on the classpath - plusConfigurations += configurations.provided - - downloadSources = true - downloadJavadoc = true - } -} - -idea { - module { - // include 'provided' dependencies on the classpath - scopes.PROVIDED.plus += configurations.provided - } -} - jar { manifest { name = 'rxjava-scala' diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala new file mode 100644 index 0000000000..67fd4ec0f1 --- /dev/null +++ b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/RxImplicits.scala @@ -0,0 +1,592 @@ +/** + * 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.scala + +object RxImplicits { + import java.{ lang => jlang } + import language.implicitConversions + + import rx.Observable + import rx.observables.BlockingObservable + import rx.util.functions._ + + /** + * Converts 0-arg function to Rx Action0 + */ + implicit def scalaFunction0ProducingUnitToAction0(f: (() => Unit)): Action0 = + new Action0 { + def call(): Unit = f() + } + + /** + * Converts 1-arg function to Rx Action1 + */ + implicit def scalaFunction1ProducingUnitToAction1[A](f: (A => Unit)): Action1[A] = + new Action1[A] { + def call(a: A): Unit = f(a) + } + + /** + * Converts 1-arg predicate to Rx Func1[A, java.lang.Boolean] + */ + implicit def scalaBooleanFunction1ToRxBooleanFunc1[A](f: (A => Boolean)): Func1[A, jlang.Boolean] = + new Func1[A, jlang.Boolean] { + def call(a: A): jlang.Boolean = f(a).booleanValue + } + + /** + * Converts a specific function shape (used in takeWhile) to the equivalent Java types with an Rx Func2 + */ + implicit def convertTakeWhileFuncToRxFunc2[A](f: (A, Int) => Boolean): Func2[A, jlang.Integer, jlang.Boolean] = + new Func2[A, jlang.Integer, jlang.Boolean] { + def call(a: A, b: jlang.Integer): jlang.Boolean = f(a, b).booleanValue + } + + /** + * Converts a function shaped ilke compareTo into the equivalent Rx Func2 + */ + implicit def convertComparisonFuncToRxFunc2[A](f: (A, A) => Int): Func2[A, A, jlang.Integer] = + new Func2[A, A, jlang.Integer] { + def call(a1: A, a2: A): jlang.Integer = f(a1, a2).intValue + } + + /* + * This implicit allows Scala code to use any exception type and still work + * with invariant Func1 interface + */ + implicit def exceptionFunction1ToRxExceptionFunc1[A <: Exception, B](f: (A => B)): Func1[Exception, B] = + new Func1[Exception, B] { + def call(ex: Exception): B = f(ex.asInstanceOf[A]) + } + + /** + * The following implicits convert functions of different arities into the Rx equivalents + */ + implicit def scalaFunction0ToRxFunc0[A](f: () => A): Func0[A] = + new Func0[A] { + def call(): A = f() + } + + implicit def scalaFunction1ToRxFunc1[A, B](f: (A => B)): Func1[A, B] = + new Func1[A, B] { + def call(a: A): B = f(a) + } + + implicit def scalaFunction2ToRxFunc2[A, B, C](f: (A, B) => C): Func2[A, B, C] = + new Func2[A, B, C] { + def call(a: A, b: B) = f(a, b) + } + + implicit def scalaFunction3ToRxFunc3[A, B, C, D](f: (A, B, C) => D): Func3[A, B, C, D] = + new Func3[A, B, C, D] { + def call(a: A, b: B, c: C) = f(a, b, c) + } + + implicit def scalaFunction4ToRxFunc4[A, B, C, D, E](f: (A, B, C, D) => E): Func4[A, B, C, D, E] = + new Func4[A, B, C, D, E] { + def call(a: A, b: B, c: C, d: D) = f(a, b, c, d) + } + + /** + * This implicit class implements all of the methods necessary for including Observables in a + * for-comprehension. Note that return type is always Observable, so that the ScalaObservable + * type never escapes the for-comprehension + */ + implicit class ScalaObservable[A](wrapped: Observable[A]) { + def map[B](f: A => B): Observable[B] = wrapped.map(f) + def flatMap[B](f: A => Observable[B]): Observable[B] = wrapped.mapMany(f) + def foreach(f: A => Unit): Unit = wrapped.toBlockingObservable.forEach(f) + def withFilter(p: A => Boolean): WithFilter = new WithFilter(p) + + class WithFilter(p: A => Boolean) { + def map[B](f: A => B): Observable[B] = wrapped.filter(p).map(f) + def flatMap[B](f: A => Observable[B]): Observable[B] = wrapped.filter(p).flatMap(f) + def foreach(f: A => Unit): Unit = wrapped.filter(p).toBlockingObservable.forEach(f) + def withFilter(p: A => Boolean): Observable[A] = wrapped.filter(p) + } + } +} + +import org.scalatest.junit.JUnitSuite + +class UnitTestSuite extends JUnitSuite { + import rx.lang.scala.RxImplicits._ + + import org.junit.{ Before, Test } + import org.junit.Assert._ + import org.mockito.Matchers.any + import org.mockito.Mockito._ + import org.mockito.{ MockitoAnnotations, Mock } + import rx.{ Notification, Observer, Observable, Subscription } + import rx.observables.GroupedObservable + import collection.mutable.ArrayBuffer + import collection.JavaConverters._ + + @Mock private[this] + val observer: Observer[Any] = null + + @Mock private[this] + val subscription: Subscription = null + + val isOdd = (i: Int) => i % 2 == 1 + val isEven = (i: Int) => i % 2 == 0 + + class ObservableWithException(s: Subscription, values: String*) extends Observable[String] { + var t: Thread = null + + override def subscribe(observer: Observer[String]): Subscription = { + println("ObservableWithException subscribed to ...") + t = new Thread(new Runnable() { + override def run() { + try { + println("running ObservableWithException thread") + values.toList.foreach(v => { + println("ObservableWithException onNext: " + v) + observer.onNext(v) + }) + throw new RuntimeException("Forced Failure") + } catch { + case ex: Exception => observer.onError(ex) + } + } + }) + println("starting ObservableWithException thread") + t.start() + println("done starting ObservableWithException thread") + s + } + } + + @Before def before { + MockitoAnnotations.initMocks(this) + } + + // tests of static methods + + @Test def testSingle { + assertEquals(1, Observable.from(1).toBlockingObservable.single) + } + + @Test def testSinglePredicate { + val found = Observable.from(1, 2, 3).toBlockingObservable.single(isEven) + assertEquals(2, found) + } + + @Test def testSingleOrDefault { + assertEquals(0, Observable.from[Int]().toBlockingObservable.singleOrDefault(0)) + assertEquals(1, Observable.from(1).toBlockingObservable.singleOrDefault(0)) + try { + Observable.from(1, 2, 3).toBlockingObservable.singleOrDefault(0) + fail("Did not catch any exception, expected IllegalStateException") + } catch { + case ex: IllegalStateException => println("Caught expected IllegalStateException") + case ex: Throwable => fail("Caught unexpected exception " + ex.getCause + ", expected IllegalStateException") + } + } + + @Test def testSingleOrDefaultPredicate { + assertEquals(2, Observable.from(1, 2, 3).toBlockingObservable.singleOrDefault(0, isEven)) + assertEquals(0, Observable.from(1, 3).toBlockingObservable.singleOrDefault(0, isEven)) + try { + Observable.from(1, 2, 3).toBlockingObservable.singleOrDefault(0, isOdd) + fail("Did not catch any exception, expected IllegalStateException") + } catch { + case ex: IllegalStateException => println("Caught expected IllegalStateException") + case ex: Throwable => fail("Caught unexpected exception " + ex.getCause + ", expected IllegalStateException") + } + } + + @Test def testFromJavaInterop { + val observable = Observable.from(List(1, 2, 3).asJava) + assertSubscribeReceives(observable)(1, 2, 3) + } + + @Test def testSubscribe { + val observable = Observable.from("1", "2", "3") + assertSubscribeReceives(observable)("1", "2", "3") + } + + //should not compile - adapted from https://gist.github.com/jmhofer/5195589 + /*@Test def testSubscribeOnInt() { + val observable = Observable.from("1", "2", "3") + observable.subscribe((arg: Int) => { + println("testSubscribe: arg = " + arg) + }) + }*/ + + @Test def testDefer { + val lazyObservableFactory = () => Observable.from(1, 2) + val observable = Observable.defer(lazyObservableFactory) + assertSubscribeReceives(observable)(1, 2) + } + + @Test def testJust { + val observable = Observable.just("foo") + assertSubscribeReceives(observable)("foo") + } + + @Test def testMerge { + val observable1 = Observable.from(1, 2, 3) + val observable2 = Observable.from(4, 5, 6) + val observableList = List(observable1, observable2).asJava + val merged = Observable.merge(observableList) + assertSubscribeReceives(merged)(1, 2, 3, 4, 5, 6) + } + + @Test def testFlattenMerge { + val observable = Observable.from(Observable.from(1, 2, 3)) + val merged = Observable.merge(observable) + assertSubscribeReceives(merged)(1, 2, 3) + } + + @Test def testSequenceMerge { + val observable1 = Observable.from(1, 2, 3) + val observable2 = Observable.from(4, 5, 6) + val merged = Observable.merge(observable1, observable2) + assertSubscribeReceives(merged)(1, 2, 3, 4, 5, 6) + } + + @Test def testConcat { + val observable1 = Observable.from(1, 2, 3) + val observable2 = Observable.from(4, 5, 6) + val concatenated = Observable.concat(observable1, observable2) + assertSubscribeReceives(concatenated)(1, 2, 3, 4, 5, 6) + } + + @Test def testSynchronize { + val observable = Observable.from(1, 2, 3) + val synchronized = Observable.synchronize(observable) + assertSubscribeReceives(synchronized)(1, 2, 3) + } + + @Test def testZip3() { + val numbers = Observable.from(1, 2, 3) + val colors = Observable.from("red", "green", "blue") + val names = Observable.from("lion-o", "cheetara", "panthro") + + case class Character(id: Int, color: String, name: String) + + val liono = Character(1, "red", "lion-o") + val cheetara = Character(2, "green", "cheetara") + val panthro = Character(3, "blue", "panthro") + + val characters = Observable.zip(numbers, colors, names, Character.apply _) + assertSubscribeReceives(characters)(liono, cheetara, panthro) + } + + @Test def testZip4() { + val numbers = Observable.from(1, 2, 3) + val colors = Observable.from("red", "green", "blue") + val names = Observable.from("lion-o", "cheetara", "panthro") + val isLeader = Observable.from(true, false, false) + + case class Character(id: Int, color: String, name: String, isLeader: Boolean) + + val liono = Character(1, "red", "lion-o", true) + val cheetara = Character(2, "green", "cheetara", false) + val panthro = Character(3, "blue", "panthro", false) + + val characters = Observable.zip(numbers, colors, names, isLeader, Character.apply _) + assertSubscribeReceives(characters)(liono, cheetara, panthro) + } + + //tests of instance methods + + // missing tests for : takeUntil, groupBy, next, mostRecent + + @Test def testFilter { + val numbers = Observable.from(1, 2, 3, 4, 5, 6, 7, 8, 9) + val observable = numbers.filter(isEven) + assertSubscribeReceives(observable)(2, 4, 6, 8) + } + + @Test def testLast { + val observable = Observable.from(1, 2, 3, 4).toBlockingObservable + assertEquals(4, observable.toBlockingObservable.last) + } + + @Test def testLastPredicate { + val observable = Observable.from(1, 2, 3, 4) + assertEquals(3, observable.toBlockingObservable.last(isOdd)) + } + + @Test def testLastOrDefault { + val observable = Observable.from(1, 2, 3, 4) + assertEquals(4, observable.toBlockingObservable.lastOrDefault(5)) + assertEquals(5, Observable.from[Int]().toBlockingObservable.lastOrDefault(5)) + } + + @Test def testLastOrDefaultPredicate { + val observable = Observable.from(1, 2, 3, 4) + assertEquals(3, observable.toBlockingObservable.lastOrDefault(5, isOdd)) + assertEquals(5, Observable.from[Int]().toBlockingObservable.lastOrDefault(5, isOdd)) + } + + @Test def testMap { + val numbers = Observable.from(1, 2, 3, 4, 5, 6, 7, 8, 9) + val mappedNumbers = ArrayBuffer.empty[Int] + numbers.map((x: Int) => x * x).subscribe((squareVal: Int) => { + mappedNumbers.append(squareVal) + }) + assertEquals(List(1, 4, 9, 16, 25, 36, 49, 64, 81), mappedNumbers.toList) + } + + @Test def testMapMany { + val numbers = Observable.from(1, 2, 3, 4) + val f = (i: Int) => Observable.from(List(i, -i).asJava) + val mappedNumbers = ArrayBuffer.empty[Int] + numbers.mapMany(f).subscribe((i: Int) => { + mappedNumbers.append(i) + }) + assertEquals(List(1, -1, 2, -2, 3, -3, 4, -4), mappedNumbers.toList) + } + + @Test def testMaterialize { + val observable = Observable.from(1, 2, 3, 4) + val expectedNotifications: List[Notification[Int]] = + ((1.to(4).map(i => new Notification(i))) :+ new Notification()).toList + val actualNotifications: ArrayBuffer[Notification[Int]] = ArrayBuffer.empty + observable.materialize.subscribe((n: Notification[Int]) => { + actualNotifications.append(n) + }) + assertEquals(expectedNotifications, actualNotifications.toList) + } + + @Test def testDematerialize { + val notifications: List[Notification[Int]] = + ((1.to(4).map(i => new Notification(i))) :+ new Notification()).toList + val observableNotifications: Observable[Notification[Int]] = + Observable.from(notifications.asJava) + val observable: Observable[Int] = + observableNotifications.dematerialize() + assertSubscribeReceives(observable)(1, 2, 3, 4) + } + + @Test def testOnErrorResumeNextObservableNoError { + val observable = Observable.from(1, 2, 3, 4) + val resumeObservable = Observable.from(5, 6, 7, 8) + val observableWithErrorHandler = observable.onErrorResumeNext(resumeObservable) + assertSubscribeReceives(observableWithErrorHandler)(1, 2, 3, 4) + } + + @Test def testOnErrorResumeNextObservableErrorOccurs { + val observable = new ObservableWithException(subscription, "foo", "bar") + val resumeObservable = Observable.from("a", "b", "c", "d") + val observableWithErrorHandler = observable.onErrorResumeNext(resumeObservable) + observableWithErrorHandler.subscribe(observer.asInstanceOf[Observer[String]]) + + try { + observable.t.join() + } catch { + case ex: InterruptedException => fail(ex.getMessage) + } + + List("foo", "bar", "a", "b", "c", "d").foreach(t => verify(observer, times(1)).onNext(t)) + verify(observer, never()).onError(any(classOf[Exception])) + verify(observer, times(1)).onCompleted() + } + + @Test def testOnErrorResumeNextFuncNoError { + val observable = Observable.from(1, 2, 3, 4) + val resumeFunc = (ex: Throwable) => Observable.from(5, 6, 7, 8) + val observableWithErrorHandler = observable.onErrorResumeNext(resumeFunc) + assertSubscribeReceives(observableWithErrorHandler)(1, 2, 3, 4) + } + + @Test def testOnErrorResumeNextFuncErrorOccurs { + val observable = new ObservableWithException(subscription, "foo", "bar") + val resumeFunc = (ex: Throwable) => Observable.from("a", "b", "c", "d") + val observableWithErrorHandler = observable.onErrorResumeNext(resumeFunc) + observableWithErrorHandler.subscribe(observer.asInstanceOf[Observer[String]]) + + try { + observable.t.join() + } catch { + case ex: InterruptedException => fail(ex.getMessage) + } + + List("foo", "bar", "a", "b", "c", "d").foreach(t => verify(observer, times(1)).onNext(t)) + verify(observer, never()).onError(any(classOf[Exception])) + verify(observer, times(1)).onCompleted() + } + + @Test def testOnErrorReturnFuncNoError { + val observable = Observable.from(1, 2, 3, 4) + val returnFunc = (ex: Throwable) => 87 + val observableWithErrorHandler = observable.onErrorReturn(returnFunc) + assertSubscribeReceives(observableWithErrorHandler)(1, 2, 3, 4) + } + + @Test def testOnErrorReturnFuncErrorOccurs { + val observable = new ObservableWithException(subscription, "foo", "bar") + val returnFunc = (ex: Throwable) => "baz" + val observableWithErrorHandler = observable.onErrorReturn(returnFunc) + observableWithErrorHandler.subscribe(observer.asInstanceOf[Observer[String]]) + + try { + observable.t.join() + } catch { + case ex: InterruptedException => fail(ex.getMessage) + } + + List("foo", "bar", "baz").foreach(t => verify(observer, times(1)).onNext(t)) + verify(observer, never()).onError(any(classOf[Exception])) + verify(observer, times(1)).onCompleted() + } + + @Test def testReduce { + val observable = Observable.from(1, 2, 3, 4) + assertEquals(10, observable.reduce((a: Int, b: Int) => a + b).toBlockingObservable.single) + } + + @Test def testSkip { + val observable = Observable.from(1, 2, 3, 4) + val skipped = observable.skip(2) + assertSubscribeReceives(skipped)(3, 4) + } + + /** + * Both testTake and testTakeWhileWithIndex exposed a bug with unsubscribes not properly propagating. + * observable.take(2) produces onNext(first), onNext(second), and 4 onCompleteds + * it should produce onNext(first), onNext(second), and 1 onCompleted + * + * Switching to Observable.create(OperationTake.take(observable, 2)) works as expected + */ + @Test def testTake { + import rx.operators._ + + val observable = Observable.from(1, 2, 3, 4, 5) + val took = Observable.create(OperationTake.take(observable, 2)) + assertSubscribeReceives(took)(1, 2) + } + + @Test def testTakeWhile { + val observable = Observable.from(1, 3, 5, 6, 7, 9, 11) + val took = observable.takeWhile(isOdd) + assertSubscribeReceives(took)(1, 3, 5) + } + + /*@Test def testTakeWhileWithIndex { + val observable = Observable.from(1, 3, 5, 6, 7, 9, 11, 12, 13, 15, 17) + val took = observable.takeWhileWithIndex((i: Int, idx: Int) => isOdd(i) && idx > 4) + assertSubscribeReceives(took)(9, 11) + }*/ + + @Test def testTakeLast { + val observable = Observable.from(1, 2, 3, 4, 5, 6, 7, 8, 9) + val tookLast = observable.takeLast(3) + assertSubscribeReceives(tookLast)(7, 8, 9) + } + + @Test def testToList { + val observable = Observable.from(1, 2, 3, 4) + val toList = observable.toList + assertSubscribeReceives(toList)(List(1, 2, 3, 4).asJava) + } + + @Test def testToSortedList { + val observable = Observable.from(1, 3, 4, 2) + val toSortedList = observable.toSortedList + assertSubscribeReceives(toSortedList)(List(1, 2, 3, 4).asJava) + } + + @Test def testToArbitrarySortedList { + val observable = Observable.from("a", "aaa", "aaaa", "aa") + val sortByLength = (s1: String, s2: String) => s1.length.compareTo(s2.length) + val toSortedList = observable.toSortedList(sortByLength) + assertSubscribeReceives(toSortedList)(List("a", "aa", "aaa", "aaaa").asJava) + } + + @Test def testToIterable { + val observable = Observable.from(1, 2) + val it = observable.toBlockingObservable.toIterable.iterator + assertTrue(it.hasNext) + assertEquals(1, it.next) + assertTrue(it.hasNext) + assertEquals(2, it.next) + assertFalse(it.hasNext) + } + + @Test def testStartWith { + val observable = Observable.from(1, 2, 3, 4) + val newStart = observable.startWith(-1, 0) + assertSubscribeReceives(newStart)(-1, 0, 1, 2, 3, 4) + } + + @Test def testOneLineForComprehension { + val mappedObservable = for { + i: Int <- Observable.from(1, 2, 3, 4) + } yield i + 1 + assertSubscribeReceives(mappedObservable)(2, 3, 4, 5) + assertFalse(mappedObservable.isInstanceOf[ScalaObservable[_]]) + } + + @Test def testSimpleMultiLineForComprehension { + val flatMappedObservable = for { + i: Int <- Observable.from(1, 2, 3, 4) + j: Int <- Observable.from(1, 10, 100, 1000) + } yield i + j + assertSubscribeReceives(flatMappedObservable)(2, 12, 103, 1004) + assertFalse(flatMappedObservable.isInstanceOf[ScalaObservable[_]]) + } + + @Test def testMultiLineForComprehension { + val doubler = (i: Int) => Observable.from(i, i) + val flatMappedObservable = for { + i: Int <- Observable.from(1, 2, 3, 4) + j: Int <- doubler(i) + } yield j + //can't use assertSubscribeReceives since each number comes in 2x + flatMappedObservable.subscribe(observer.asInstanceOf[Observer[Int]]) + List(1, 2, 3, 4).foreach(i => verify(observer, times(2)).onNext(i)) + verify(observer, never()).onError(any(classOf[Exception])) + verify(observer, times(1)).onCompleted() + assertFalse(flatMappedObservable.isInstanceOf[ScalaObservable[_]]) + } + + @Test def testFilterInForComprehension { + val doubler = (i: Int) => Observable.from(i, i) + val filteredObservable = for { + i: Int <- Observable.from(1, 2, 3, 4) + j: Int <- doubler(i) if isOdd(i) + } yield j + //can't use assertSubscribeReceives since each number comes in 2x + filteredObservable.subscribe(observer.asInstanceOf[Observer[Int]]) + List(1, 3).foreach(i => verify(observer, times(2)).onNext(i)) + verify(observer, never()).onError(any(classOf[Exception])) + verify(observer, times(1)).onCompleted() + assertFalse(filteredObservable.isInstanceOf[ScalaObservable[_]]) + } + + @Test def testForEachForComprehension { + val doubler = (i: Int) => Observable.from(i, i) + val intBuffer = ArrayBuffer.empty[Int] + val forEachComprehension = for { + i: Int <- Observable.from(1, 2, 3, 4) + j: Int <- doubler(i) if isEven(i) + } { + intBuffer.append(j) + } + assertEquals(List(2, 2, 4, 4), intBuffer.toList) + } + + private def assertSubscribeReceives[T](o: Observable[T])(values: T*) = { + o.subscribe(observer.asInstanceOf[Observer[T]]) + values.toList.foreach(t => verify(observer, times(1)).onNext(t)) + verify(observer, never()).onError(any(classOf[Exception])) + verify(observer, times(1)).onCompleted() + } +} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ScalaAdaptor.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ScalaAdaptor.scala deleted file mode 100644 index 12418d516d..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ScalaAdaptor.scala +++ /dev/null @@ -1,204 +0,0 @@ -/** - * 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.scala - -import rx.util.functions.FunctionLanguageAdaptor -import org.junit.{Assert, Before, Test} -import rx.Observable -import org.scalatest.junit.JUnitSuite -import org.mockito.Mockito._ -import org.mockito.{MockitoAnnotations, Mock} - -import scala.collection.JavaConverters._ -import collection.mutable.ArrayBuffer - -class ScalaAdaptor extends FunctionLanguageAdaptor { - - val ON_NEXT = "onNext" - val ON_ERROR = "onError" - val ON_COMPLETED = "onCompleted" - - def getFunctionClass: Array[Class[_]] = { - return Array(classOf[Map[String, _]], classOf[(AnyRef) => Object], classOf[(AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef) => Object], classOf[(AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) =>Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object], - classOf[(AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object]) - } - - def call(function: AnyRef, args: Array[AnyRef]) : Object = { - function match { - case (func: Map[String, _]) => return matchOption(func.get(ON_NEXT), args) - case _ => return matchFunction(function, args) - } - } - - private def matchOption(funcOption: Option[_], args: Array[AnyRef]) : Object = { - funcOption match { - case Some(func: AnyRef) => return matchFunction(func, args) - case _ => return None - } - } - - private def matchFunction(function: AnyRef, args: Array[AnyRef]) : Object = function match { - case (f: ((AnyRef) => Object)) => return f(args(0)) - case (f: ((AnyRef, AnyRef) => Object)) => return f(args(0), args(1)) - case (f: ((AnyRef, AnyRef, AnyRef) => Object)) => return f(args(0), args(1), args(2)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11), args(12)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11), args(12), args(13)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11), args(12), args(13), args(14)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11), args(12), args(13), args(14), args(15)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11), args(12), args(13), args(14), args(15), args(16)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11), args(12), args(13), args(14), args(15), args(16), args(17)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11), args(12), args(13), args(14), args(15), args(16), args(17), args(18)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11), args(12), args(13), args(14), args(15), args(16), args(17), args(18), args(19)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11), args(12), args(13), args(14), args(15), args(16), args(17), args(18), args(19), args(20)) - case (f: ((AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef, AnyRef) => Object)) => - return f(args(0), args(1), args(2), args(3), args(4), args(5), args(6), args(7), args(8), args(9), args(10), args(11), args(12), args(13), args(14), args(15), args(16), args(17), args(18), args(19), args(20), args(21)) - - } -} - -class UnitTestSuite extends JUnitSuite { - @Mock private[this] - val assertion: ScriptAssertion = null - - @Before def before { - MockitoAnnotations.initMocks(this) - } - - @Test def testTake() { - Observable.from("1", "2", "3").take(1).subscribe(Map( - "onNext" -> ((callback: String) => { - print("testTake: callback = " + callback) - assertion.received(callback) - }) - )) - verify(assertion, times(1)).received("1") - } - - @Test def testClosureVersusMap() { - // using closure - Observable.from("1", "2", "3") - .take(2) - .subscribe((callback: String) => { - println(callback) - }) - - // using Map of closures - Observable.from("1", "2", "3") - .take(2) - .subscribe(Map( - "onNext" -> ((callback: String) => { - println(callback) - }))) - } - - @Test def testFilterWithToList() { - val numbers = Observable.from[Int](1, 2, 3, 4, 5, 6, 7, 8, 9) - numbers.filter((x: Int) => 0 == (x % 2)).toList().subscribe( - (callback: java.util.List[Int]) => { - val lst = callback.asScala.toList - println("filter onNext -> got " + lst) - assertion.received(lst) - } - ) - verify(assertion, times(1)).received(List(2,4,6,8)) - } - - @Test def testTakeLast() { - val numbers = Observable.from[Int](1, 2, 3, 4, 5, 6, 7, 8, 9) - numbers.takeLast(1).subscribe((callback: Int) => { - println("testTakeLast: onNext -> got " + callback) - assertion.received(callback) - }) - verify(assertion, times(1)).received(9) - } - - @Test def testMap() { - val numbers = Observable.from(1, 2, 3, 4, 5, 6, 7, 8, 9) - val mappedNumbers = new ArrayBuffer[Int]() - numbers.map(((x: Int)=> { x * x })).subscribe(((squareVal: Int) => { - println("square is " + squareVal ) - mappedNumbers += squareVal - })) - Assert.assertEquals(List(1,4,9,16,25,36,49,64,81), mappedNumbers.toList) - - } - - @Test def testZip() { - val numbers = Observable.from(1, 2, 3) - val colors = Observable.from("red", "green", "blue") - val characters = Observable.from("lion-o", "cheetara", "panthro") - - Observable.zip(numbers.toList, colors.toList, characters.toList, ((n: java.util.List[Int], c: java.util.List[String], t: java.util.List[String]) => { Map( - "numbers" -> n, - "colors" -> c, - "thundercats" -> t - )})).subscribe((m: Map[String, _]) => { - println("zipped map is " + m.toString()) - }) - - - } - - trait ScriptAssertion { - def error(ex: Exception) - - def received(obj: Any) - } -} diff --git a/rxjava-contrib/rxjava-android/build.gradle b/rxjava-contrib/rxjava-android/build.gradle index 96bbb27267..f3b7745c81 100644 --- a/rxjava-contrib/rxjava-android/build.gradle +++ b/rxjava-contrib/rxjava-android/build.gradle @@ -1,11 +1,5 @@ -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' apply plugin: 'osgi' -sourceCompatibility = JavaVersion.VERSION_1_6 -targetCompatibility = JavaVersion.VERSION_1_6 - dependencies { compile project(':rxjava-core') provided 'junit:junit-dep:4.10' @@ -14,23 +8,6 @@ dependencies { provided 'com.google.android:android:4.0.1.2' } -eclipse { - classpath { - // include 'provided' dependencies on the classpath - plusConfigurations += configurations.provided - - downloadSources = true - downloadJavadoc = true - } -} - -idea { - module { - // include 'provided' dependencies on the classpath - scopes.PROVIDED.plus += configurations.provided - } -} - javadoc { options { doclet = "org.benjchristensen.doclet.DocletExclude" diff --git a/rxjava-contrib/rxjava-swing/build.gradle b/rxjava-contrib/rxjava-swing/build.gradle index 986f7ca6b9..ea863813a2 100644 --- a/rxjava-contrib/rxjava-swing/build.gradle +++ b/rxjava-contrib/rxjava-swing/build.gradle @@ -1,6 +1,3 @@ -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' apply plugin: 'osgi' sourceCompatibility = JavaVersion.VERSION_1_6 @@ -12,23 +9,6 @@ dependencies { provided 'org.mockito:mockito-core:1.8.5' } -eclipse { - classpath { - // include 'provided' dependencies on the classpath - plusConfigurations += configurations.provided - - downloadSources = true - downloadJavadoc = true - } -} - -idea { - module { - // include 'provided' dependencies on the classpath - scopes.PROVIDED.plus += configurations.provided - } -} - javadoc { options { doclet = "org.benjchristensen.doclet.DocletExclude" diff --git a/rxjava-contrib/rxjava-swing/src/main/java/rx/concurrency/SwingScheduler.java b/rxjava-contrib/rxjava-swing/src/main/java/rx/concurrency/SwingScheduler.java index ced759f6f5..2ce298c590 100644 --- a/rxjava-contrib/rxjava-swing/src/main/java/rx/concurrency/SwingScheduler.java +++ b/rxjava-contrib/rxjava-swing/src/main/java/rx/concurrency/SwingScheduler.java @@ -38,7 +38,6 @@ import rx.subscriptions.CompositeSubscription; import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; -import rx.util.functions.Func0; import rx.util.functions.Func2; /** @@ -187,14 +186,12 @@ public void testPeriodicScheduling() throws Exception { final CountDownLatch latch = new CountDownLatch(4); final Action0 innerAction = mock(Action0.class); - final Action0 unsubscribe = mock(Action0.class); - final Func0 action = new Func0() { + final Action0 action = new Action0() { @Override - public Subscription call() { + public void call() { try { innerAction.call(); assertTrue(SwingUtilities.isEventDispatchThread()); - return Subscriptions.create(unsubscribe); } finally { latch.countDown(); } @@ -210,7 +207,6 @@ public Subscription call() { sub.unsubscribe(); waitForEmptyEventQueue(); verify(innerAction, times(4)).call(); - verify(unsubscribe, times(4)).call(); } @Test diff --git a/rxjava-contrib/rxjava-swing/src/main/java/rx/observables/SwingObservable.java b/rxjava-contrib/rxjava-swing/src/main/java/rx/observables/SwingObservable.java index fa9ecd3095..b280638191 100644 --- a/rxjava-contrib/rxjava-swing/src/main/java/rx/observables/SwingObservable.java +++ b/rxjava-contrib/rxjava-swing/src/main/java/rx/observables/SwingObservable.java @@ -15,8 +15,6 @@ */ package rx.observables; -import static rx.Observable.filter; - import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; @@ -69,7 +67,7 @@ public static Observable fromKeyEvents(Component component) { * @return Observable of key events. */ public static Observable fromKeyEvents(Component component, final Set keyCodes) { - return filter(fromKeyEvents(component), new Func1() { + return fromKeyEvents(component).filter(new Func1() { @Override public Boolean call(KeyEvent event) { return keyCodes.contains(event.getKeyCode()); diff --git a/rxjava-contrib/rxjava-swing/src/main/java/rx/swing/sources/AbstractButtonSource.java b/rxjava-contrib/rxjava-swing/src/main/java/rx/swing/sources/AbstractButtonSource.java index 22a2ce78fc..ef3612b080 100644 --- a/rxjava-contrib/rxjava-swing/src/main/java/rx/swing/sources/AbstractButtonSource.java +++ b/rxjava-contrib/rxjava-swing/src/main/java/rx/swing/sources/AbstractButtonSource.java @@ -69,7 +69,7 @@ public void testObservingActionEvents() { @SuppressWarnings("unchecked") Action1 action = mock(Action1.class); @SuppressWarnings("unchecked") - Action1 error = mock(Action1.class); + Action1 error = mock(Action1.class); Action0 complete = mock(Action0.class); final ActionEvent event = new ActionEvent(this, 1, "command"); @@ -85,7 +85,7 @@ void testAction() { Subscription sub = fromActionOf(button).subscribe(action, error, complete); verify(action, never()).call(Matchers.any()); - verify(error, never()).call(Matchers.any()); + verify(error, never()).call(Matchers.any()); verify(complete, never()).call(); button.testAction(); @@ -97,7 +97,7 @@ void testAction() { sub.unsubscribe(); button.testAction(); verify(action, times(2)).call(Matchers.any()); - verify(error, never()).call(Matchers.any()); + verify(error, never()).call(Matchers.any()); verify(complete, never()).call(); } } diff --git a/rxjava-contrib/rxjava-swing/src/main/java/rx/swing/sources/KeyEventSource.java b/rxjava-contrib/rxjava-swing/src/main/java/rx/swing/sources/KeyEventSource.java index d6f294cebe..291e0202aa 100644 --- a/rxjava-contrib/rxjava-swing/src/main/java/rx/swing/sources/KeyEventSource.java +++ b/rxjava-contrib/rxjava-swing/src/main/java/rx/swing/sources/KeyEventSource.java @@ -85,7 +85,7 @@ public void call() { * @see SwingObservable.fromKeyEvents(Component, Set) */ public static Observable> currentlyPressedKeysOf(Component component) { - return Observable.>scan(fromKeyEventsOf(component), new HashSet(), new Func2, KeyEvent, Set>() { + return fromKeyEventsOf(component).>scan(new HashSet(), new Func2, KeyEvent, Set>() { @Override public Set call(Set pressedKeys, KeyEvent event) { Set afterEvent = new HashSet(pressedKeys); @@ -113,7 +113,7 @@ public void testObservingKeyEvents() { @SuppressWarnings("unchecked") Action1 action = mock(Action1.class); @SuppressWarnings("unchecked") - Action1 error = mock(Action1.class); + Action1 error = mock(Action1.class); Action0 complete = mock(Action0.class); final KeyEvent event = mock(KeyEvent.class); @@ -121,7 +121,7 @@ public void testObservingKeyEvents() { Subscription sub = fromKeyEventsOf(comp).subscribe(action, error, complete); verify(action, never()).call(Matchers.any()); - verify(error, never()).call(Matchers.any()); + verify(error, never()).call(Matchers.any()); verify(complete, never()).call(); fireKeyEvent(event); @@ -133,7 +133,7 @@ public void testObservingKeyEvents() { sub.unsubscribe(); fireKeyEvent(event); verify(action, times(2)).call(Matchers.any()); - verify(error, never()).call(Matchers.any()); + verify(error, never()).call(Matchers.any()); verify(complete, never()).call(); } @@ -142,19 +142,19 @@ public void testObservingPressedKeys() { @SuppressWarnings("unchecked") Action1> action = mock(Action1.class); @SuppressWarnings("unchecked") - Action1 error = mock(Action1.class); + Action1 error = mock(Action1.class); Action0 complete = mock(Action0.class); Subscription sub = currentlyPressedKeysOf(comp).subscribe(action, error, complete); InOrder inOrder = inOrder(action); inOrder.verify(action, times(1)).call(Collections.emptySet()); - verify(error, never()).call(Matchers.any()); + verify(error, never()).call(Matchers.any()); verify(complete, never()).call(); fireKeyEvent(keyEvent(1, KeyEvent.KEY_PRESSED)); inOrder.verify(action, times(1)).call(new HashSet(asList(1))); - verify(error, never()).call(Matchers.any()); + verify(error, never()).call(Matchers.any()); verify(complete, never()).call(); fireKeyEvent(keyEvent(2, KeyEvent.KEY_PRESSED)); @@ -173,7 +173,7 @@ public void testObservingPressedKeys() { fireKeyEvent(keyEvent(1, KeyEvent.KEY_PRESSED)); inOrder.verify(action, never()).call(Matchers.>any()); - verify(error, never()).call(Matchers.any()); + verify(error, never()).call(Matchers.any()); verify(complete, never()).call(); } diff --git a/rxjava-core/build.gradle b/rxjava-core/build.gradle index 72a984c88d..1732de3017 100644 --- a/rxjava-core/build.gradle +++ b/rxjava-core/build.gradle @@ -1,6 +1,4 @@ -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' +apply plugin: 'maven' apply plugin: 'osgi' sourceCompatibility = JavaVersion.VERSION_1_6 @@ -11,23 +9,6 @@ dependencies { provided 'org.mockito:mockito-core:1.8.5' } -eclipse { - classpath { - // include 'provided' dependencies on the classpath - plusConfigurations += configurations.provided - - downloadSources = true - downloadJavadoc = true - } -} - -idea { - module { - // include 'provided' dependencies on the classpath - scopes.PROVIDED.plus += configurations.provided - } -} - javadoc { // we do not want the org.rx.operations package include exclude '**/operations/**' @@ -48,7 +29,5 @@ jar { instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava' instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*' } - // commenting out for now as it's breaking the rxjava-scala build and I can't figure out why - // exclude('**/*$UnitTest*') } diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index f6431926e9..a4ddbaf452 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -1,12 +1,12 @@ /** * 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. @@ -15,33 +15,18 @@ */ package rx; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; +import rx.concurrency.Schedulers; import rx.observables.BlockingObservable; import rx.observables.ConnectableObservable; import rx.observables.GroupedObservable; -import rx.operators.OperationOnExceptionResumeNextViaObservable; -import rx.operators.SafeObservableSubscription; -import rx.operators.SafeObserver; import rx.operators.OperationAll; import rx.operators.OperationBuffer; import rx.operators.OperationCache; @@ -61,6 +46,7 @@ import rx.operators.OperationOnErrorResumeNextViaFunction; import rx.operators.OperationOnErrorResumeNextViaObservable; import rx.operators.OperationOnErrorReturn; +import rx.operators.OperationOnExceptionResumeNextViaObservable; import rx.operators.OperationSample; import rx.operators.OperationScan; import rx.operators.OperationSkip; @@ -76,21 +62,22 @@ import rx.operators.OperationToObservableIterable; import rx.operators.OperationToObservableList; import rx.operators.OperationToObservableSortedList; -import rx.operators.OperationWhere; import rx.operators.OperationZip; +import rx.operators.SafeObservableSubscription; +import rx.operators.SafeObserver; import rx.plugins.RxJavaErrorHandler; import rx.plugins.RxJavaObservableExecutionHook; import rx.plugins.RxJavaPlugins; import rx.subjects.PublishSubject; import rx.subjects.ReplaySubject; import rx.subjects.Subject; -import rx.subscriptions.BooleanSubscription; import rx.subscriptions.Subscriptions; import rx.util.BufferClosing; import rx.util.BufferOpening; import rx.util.OnErrorNotImplementedException; import rx.util.Range; import rx.util.Timestamped; +import rx.util.functions.Action; import rx.util.functions.Action0; import rx.util.functions.Action1; import rx.util.functions.Func0; @@ -100,7 +87,6 @@ import rx.util.functions.Func4; import rx.util.functions.FuncN; import rx.util.functions.Function; -import rx.util.functions.FunctionLanguageAdaptor; import rx.util.functions.Functions; /** @@ -116,12 +102,12 @@ *

* For more information see the RxJava * Wiki - * + * * @param */ public class Observable { -//TODO use a consistent parameter naming scheme (for example: for all operators that modify a source Observable, the parameter representing that source Observable should have the same name, e.g. "source" -- currently such parameters are named any of "sequence", "that", "source", "items", or "observable") + //TODO use a consistent parameter naming scheme (for example: for all operators that modify a source Observable, the parameter representing that source Observable should have the same name, e.g. "source" -- currently such parameters are named any of "sequence", "that", "source", "items", or "observable") private final static RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook(); @@ -132,7 +118,7 @@ public class Observable { *

* NOTE: Use {@link #create(Func1)} to create an Observable instead of this method unless you * specifically have a need for inheritance. - * + * * @param onSubscribe * {@link Func1} to be executed when {@link #subscribe(Observer)} is called. */ @@ -148,16 +134,14 @@ protected Observable() { /** * An {@link Observer} must call an Observable's {@code subscribe} method in order to * receive items and notifications from the Observable. - * + * *

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

- * It stores a reference to the Observer in a collection object, such as a - * {@code List} object. + * It stores a reference to the Observer in a collection object, such as a {@code List} object. *

* 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. + * sending them, which also invokes the Observer's {@link Observer#onCompleted onCompleted} method. *

* An Observable<T> instance is responsible for accepting all subscriptions * and notifying all Observers. Unless the documentation for a particular @@ -166,13 +150,13 @@ protected Observable() { *

* For more information see the * RxJava Wiki - * - * @param observer the observer + * + * @param observer + * the observer * @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 Subscription subscribe(Observer observer) { // allow the hook to intercept and/or decorate @@ -227,26 +211,24 @@ public Subscription subscribe(Observer observer) { /** * An {@link Observer} must call an Observable's {@code subscribe} method in order to * receive items and notifications from the Observable. - * + * *

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

- * It stores a reference to the Observer in a collection object, such as a - * {@code List} object. + * It stores a reference to the Observer in a collection object, such as a {@code List} object. *

* 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. + * 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 + * and 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 * RxJava Wiki - * - * @param observer the observer + * + * @param observer + * the observer * @param scheduler * the {@link Scheduler} on which Observers subscribe to the Observable * @return a {@link Subscription} reference with which Observers can stop receiving items and @@ -268,100 +250,6 @@ private Subscription protectivelyWrapAndSubscribe(Observer o) { return subscription.wrap(subscribe(new SafeObserver(subscription, o))); } - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Subscription subscribe(final Map callbacks) { - if (callbacks == null) { - throw new RuntimeException("callbacks map can not be null"); - } - Object _onNext = callbacks.get("onNext"); - if (_onNext == null) { - throw new RuntimeException("'onNext' key must contain an implementation"); - } - // lookup and memoize onNext - final FuncN onNext = Functions.from(_onNext); - - /** - * Wrapping since raw functions provided by the user are being invoked. - * - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - Object onComplete = callbacks.get("onCompleted"); - if (onComplete != null) { - Functions.from(onComplete).call(); - } - } - - @Override - public void onError(Throwable e) { - handleError(e); - Object onError = callbacks.get("onError"); - if (onError != null) { - Functions.from(onError).call(e); - } else { - throw new OnErrorNotImplementedException(e); - } - } - - @Override - public void onNext(Object args) { - onNext.call(args); - } - - }); - } - - public Subscription subscribe(final Map callbacks, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(callbacks); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Subscription subscribe(final Object o) { - if (o instanceof Observer) { - // in case a dynamic language is not correctly handling the overloaded methods and we receive an Observer just forward to the correct method. - return subscribe((Observer) o); - } - - if (o == null) { - throw new IllegalArgumentException("onNext can not be null"); - } - - // lookup and memoize onNext - final FuncN onNext = Functions.from(o); - - /** - * Wrapping since raw functions provided by the user are being invoked. - * - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - // do nothing - } - - @Override - public void onError(Throwable e) { - handleError(e); - throw new OnErrorNotImplementedException(e); - } - - @Override - public void onNext(Object args) { - onNext.call(args); - } - - }); - } - - public Subscription subscribe(final Object o, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(o); - } - public Subscription subscribe(final Action1 onNext) { if (onNext == null) { throw new IllegalArgumentException("onNext can not be null"); @@ -369,7 +257,7 @@ public Subscription subscribe(final Action1 onNext) { /** * Wrapping since raw functions provided by the user are being invoked. - * + * * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" */ return protectivelyWrapAndSubscribe(new Observer() { @@ -397,48 +285,6 @@ public Subscription subscribe(final Action1 onNext, Scheduler scheduler) { return subscribeOn(scheduler).subscribe(onNext); } - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Subscription subscribe(final Object onNext, final Object onError) { - if (onNext == null) { - throw new IllegalArgumentException("onNext can not be null"); - } - if (onError == null) { - throw new IllegalArgumentException("onError can not be null"); - } - - // lookup and memoize onNext - final FuncN onNextFunction = Functions.from(onNext); - - /** - * Wrapping since raw functions provided by the user are being invoked. - * - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - // do nothing - } - - @Override - public void onError(Throwable e) { - handleError(e); - Functions.from(onError).call(e); - } - - @Override - public void onNext(Object args) { - onNextFunction.call(args); - } - - }); - } - - public Subscription subscribe(final Object onNext, final Object onError, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(onNext, onError); - } - public Subscription subscribe(final Action1 onNext, final Action1 onError) { if (onNext == null) { throw new IllegalArgumentException("onNext can not be null"); @@ -449,7 +295,7 @@ public Subscription subscribe(final Action1 onNext, final Action1 /** * Wrapping since raw functions provided by the user are being invoked. - * + * * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" */ return protectivelyWrapAndSubscribe(new Observer() { @@ -477,51 +323,6 @@ public Subscription subscribe(final Action1 onNext, final Action1 return subscribeOn(scheduler).subscribe(onNext, onError); } - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Subscription subscribe(final Object onNext, final Object onError, final Object onComplete) { - if (onNext == null) { - throw new IllegalArgumentException("onNext can not be null"); - } - if (onError == null) { - throw new IllegalArgumentException("onError can not be null"); - } - if (onComplete == null) { - throw new IllegalArgumentException("onComplete can not be null"); - } - - // lookup and memoize onNext - final FuncN onNextFunction = Functions.from(onNext); - - /** - * Wrapping since raw functions provided by the user are being invoked. - * - * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - */ - return protectivelyWrapAndSubscribe(new Observer() { - - @Override - public void onCompleted() { - Functions.from(onComplete).call(); - } - - @Override - public void onError(Throwable e) { - handleError(e); - Functions.from(onError).call(e); - } - - @Override - public void onNext(Object args) { - onNextFunction.call(args); - } - - }); - } - - public Subscription subscribe(final Object onNext, final Object onError, final Object onComplete, Scheduler scheduler) { - return subscribeOn(scheduler).subscribe(onNext, onError, onComplete); - } - public Subscription subscribe(final Action1 onNext, final Action1 onError, final Action0 onComplete) { if (onNext == null) { throw new IllegalArgumentException("onNext can not be null"); @@ -535,7 +336,7 @@ public Subscription subscribe(final Action1 onNext, final Action1 /** * Wrapping since raw functions provided by the user are being invoked. - * + * * See https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" */ return protectivelyWrapAndSubscribe(new Observer() { @@ -566,7 +367,7 @@ public Subscription subscribe(final Action1 onNext, final Action1 /** * Returns a {@link ConnectableObservable} that upon connection causes the source Observable to * push results into the specified subject. - * + * * @param subject * the {@link Subject} for the {@link ConnectableObservable} to push source items * into @@ -576,12 +377,12 @@ public Subscription subscribe(final Action1 onNext, final Action1 * push results into the specified {@link Subject} */ public ConnectableObservable multicast(Subject subject) { - return multicast(this, subject); + return OperationMulticast.multicast(this, subject); } /** * Allow the {@link RxJavaErrorHandler} to receive the exception from onError. - * + * * @param e */ private void handleError(Throwable e) { @@ -591,9 +392,9 @@ private void handleError(Throwable e) { /** * An Observable that never sends any information to an {@link Observer}. - * + * * This Observable is useful primarily for testing purposes. - * + * * @param * the type of item emitted by the Observable */ @@ -611,9 +412,8 @@ public Subscription call(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 emitted by the Observable */ @@ -624,7 +424,7 @@ public ThrowObservable(final Throwable exception) { /** * Accepts an {@link Observer} and calls its {@link Observer#onError onError} method. - * + * * @param observer * an {@link Observer} of this Observable * @return a reference to the subscription @@ -641,499 +441,134 @@ public Subscription call(Observer observer) { } /** - * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers. The current buffer is emitted and replaced with a new buffer when the - * Observable produced by the specified {@link Func0} produces a {@link BufferClosing} object. The - * {@link Func0} will then be used to create a new Observable to listen for the end of the next buffer. - * - * @param source - * The source {@link Observable} which produces values. - * @param bufferClosingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every buffer created. - * When this {@link Observable} produces a {@link BufferClosing} object, the associated buffer - * is emitted and replaced with a new one. - * @return - * An {@link Observable} which produces connected non-overlapping buffers, which are emitted - * when the current {@link Observable} created with the {@link Func0} argument produces a - * {@link BufferClosing} object. + * Creates an Observable that will execute the given function when an {@link Observer} subscribes to it. + *

+ * + *

+ * Write the function you pass to 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 onCompleted method + * exactly once or its 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} to allow the Observer to + * canceling the subscription + * @return an Observable that, when an {@link Observer} subscribes to it, will execute the given + * function */ - public static Observable> buffer(Observable source, Func0> bufferClosingSelector) { - return create(OperationBuffer.buffer(source, bufferClosingSelector)); + public static Observable create(Func1, Subscription> func) { + return new Observable(func); } /** - * Creates an Observable which produces buffers of collected values. This Observable produces buffers. - * Buffers are created when the specified "bufferOpenings" Observable produces a {@link BufferOpening} object. - * Additionally the {@link Func0} argument is used to create an Observable which produces {@link BufferClosing} - * objects. When this Observable produces such an object, the associated buffer is emitted. - * - * @param source - * The source {@link Observable} which produces values. - * @param bufferOpenings - * The {@link Observable} which when it produces a {@link BufferOpening} object, will cause - * another buffer to be created. - * @param bufferClosingSelector - * The {@link Func0} which is used to produce an {@link Observable} for every buffer created. - * When this {@link Observable} produces a {@link BufferClosing} object, the associated buffer - * is emitted. - * @return - * An {@link Observable} which produces buffers which are created and emitted when the specified - * {@link Observable}s publish certain objects. + * Returns an Observable that emits no data to the {@link Observer} and immediately invokes + * its {@link Observer#onCompleted onCompleted} method. + *

+ * + * + * @param + * the type of the items (ostensibly) emitted by the Observable + * @return an Observable that returns no data to the {@link Observer} and immediately invokes + * the {@link Observer}'s {@link Observer#onCompleted() onCompleted} method */ - public static Observable> buffer(Observable source, Observable bufferOpenings, Func1> bufferClosingSelector) { - return create(OperationBuffer.buffer(source, bufferOpenings, bufferClosingSelector)); + public static Observable empty() { + return from(new ArrayList()); } /** - * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each containing "count" elements. When the source Observable completes or - * encounters an error, the current buffer is emitted, and the event is propagated. - * - * @param source - * The source {@link Observable} which produces values. - * @param count - * The maximum size of each buffer before it should be emitted. - * @return - * An {@link Observable} which produces connected non-overlapping buffers containing at most - * "count" produced values. + * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method when the Observer subscribes to it + *

+ * + * + * @param exception + * the particular error to report + * @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 */ - public static Observable> buffer(Observable source, int count) { - return create(OperationBuffer.buffer(source, count)); + public static Observable error(Throwable exception) { + return new ThrowObservable(exception); } /** - * Creates an Observable which produces buffers of collected values. This Observable produces buffers every - * "skip" values, each containing "count" elements. When the source Observable completes or encounters an error, - * the current buffer is emitted and the event is propagated. - * - * @param source - * The source {@link Observable} which produces values. - * @param count - * The maximum size of each buffer before it should be emitted. - * @param skip - * How many produced values need to be skipped before starting a new buffer. Note that when "skip" and - * "count" are equals that this is the same operation as {@link Observable#buffer(Observable, int)}. - * @return - * An {@link Observable} which produces buffers every "skipped" values containing at most - * "count" produced values. + * Converts an {@link Iterable} sequence into an Observable. + *

+ * + * + *

Implementation note: the entire iterable sequence will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, + * it in not possible to unsubscribe from the sequence before it completes. + * + * @param iterable + * the source {@link Iterable} sequence + * @param + * the type of items in the {@link Iterable} sequence and the type of items to be + * emitted by the resulting Observable + * @return an Observable that emits each item in the source {@link Iterable} sequence */ - public static Observable> buffer(Observable source, int count, int skip) { - return create(OperationBuffer.buffer(source, count, skip)); + public static Observable from(Iterable iterable) { + return create(OperationToObservableIterable.toObservableIterable(iterable)); } /** - * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument. When the source - * Observable completes or encounters an error, the current buffer is emitted and the event is propagated. - * - * @param source - * The source {@link Observable} which produces values. - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @return - * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. + * Converts an Array into an Observable. + *

+ * + * + *

Implementation note: the entire array will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, + * it in not possible to unsubscribe from the sequence before it completes. + * + * @param items + * the source Array + * @param + * the type of items in the Array, and the type of items to be emitted by the + * resulting Observable + * @return an Observable that emits each item in the source Array */ - public static Observable> buffer(Observable source, long timespan, TimeUnit unit) { - return create(OperationBuffer.buffer(source, timespan, unit)); - } - - /** - * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument. When the source - * Observable completes or encounters an error, the current buffer is emitted and the event is propagated. - * - * @param source - * The source {@link Observable} which produces values. - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. - * @return - * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. - */ - public static Observable> buffer(Observable source, long timespan, TimeUnit unit, Scheduler scheduler) { - return create(OperationBuffer.buffer(source, timespan, unit, scheduler)); - } - - /** - * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes - * or encounters an error, the current buffer is emitted and the event is propagated. - * - * @param source - * The source {@link Observable} which produces values. - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param count - * The maximum size of each buffer before it should be emitted. - * @return - * An {@link Observable} which produces connected non-overlapping buffers which are emitted after - * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). - */ - public static Observable> buffer(Observable source, long timespan, TimeUnit unit, int count) { - return create(OperationBuffer.buffer(source, timespan, unit, count)); - } - - /** - * Creates an Observable which produces buffers of collected values. This Observable produces connected - * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size - * specified by the "count" argument (which ever is reached first). When the source Observable completes - * or encounters an error, the current buffer is emitted and the event is propagated. - * - * @param source - * The source {@link Observable} which produces values. - * @param timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param unit - * The unit of time which applies to the "timespan" argument. - * @param count - * The maximum size of each buffer before it should be emitted. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. - * @return - * An {@link Observable} which produces connected non-overlapping buffers which are emitted after - * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). - */ - public static Observable> buffer(Observable source, long timespan, TimeUnit unit, int count, Scheduler scheduler) { - return create(OperationBuffer.buffer(source, timespan, unit, count, scheduler)); - } - - /** - * Creates an Observable which produces buffers of collected values. This Observable starts a new buffer - * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the - * current buffer is emitted and the event is propagated. - * - * @param source - * The source {@link Observable} which produces values. - * @param timespan - * The period of time each buffer is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new buffer will be created. - * @param unit - * The unit of time which applies to the "timespan" and "timeshift" argument. - * @return - * An {@link Observable} which produces new buffers periodically, and these are emitted after - * a fixed timespan has elapsed. - */ - public static Observable> buffer(Observable source, long timespan, long timeshift, TimeUnit unit) { - return create(OperationBuffer.buffer(source, timespan, timeshift, unit)); - } - - /** - * Creates an Observable which produces buffers of collected values. This Observable starts a new buffer - * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan - * specified by the "timespan" argument. When the source Observable completes or encounters an error, the - * current buffer is emitted and the event is propagated. - * - * @param source - * The source {@link Observable} which produces values. - * @param timespan - * The period of time each buffer is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new buffer will be created. - * @param unit - * The unit of time which applies to the "timespan" and "timeshift" argument. - * @param scheduler - * The {@link Scheduler} to use when determining the end and start of a buffer. - * @return - * An {@link Observable} which produces new buffers periodically, and these are emitted after - * a fixed timespan has elapsed. - */ - public static Observable> buffer(Observable source, long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { - return create(OperationBuffer.buffer(source, timespan, timeshift, unit, scheduler)); - } - - /** - * Creates an Observable that will execute the given function when an {@link Observer} - * subscribes to it. - *

- * - *

- * Write the function you pass to 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 onCompleted method - * exactly once or its 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} to allow the Observer to - * canceling the subscription - * @return an Observable that, when an {@link Observer} subscribes to it, will execute the given - * function - */ - public static Observable create(Func1, Subscription> func) { - return new Observable(func); - } - - /** - * Creates an Observable that will execute the given function when an {@link Observer} - * subscribes to it. - *

- * - *

- * This method accepts {@link Object} to allow different languages to pass in methods using - * {@link FunctionLanguageAdaptor}. - *

- * Write the function you pass to 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 onCompleted method - * exactly once or its 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 given - * function - */ - public static Observable create(final Object func) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(func); - return create(new Func1, Subscription>() { - - @Override - public Subscription call(Observer t1) { - return (Subscription) _f.call(t1); - } - - }); - } - - /** - * Returns an Observable that emits no data to the {@link Observer} and immediately invokes - * its {@link Observer#onCompleted onCompleted} method. - *

- * - * - * @param - * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that returns no data to the {@link Observer} and immediately invokes - * the {@link Observer}'s {@link Observer#onCompleted() onCompleted} method - */ - public static Observable empty() { - return toObservable(new ArrayList()); - } - - /** - * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} - * method when the Observer subscribes to it - *

- * - * - * @param exception - * the particular error to report - * @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 - */ - public static Observable error(Throwable exception) { - return new ThrowObservable(exception); - } - - /** - * Filters an Observable by discarding any items it emits that do not satisfy some predicate. - *

- * - * - * @param that - * the Observable to filter - * @param predicate - * 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 for which the - * predicate evaluates to {@code true} - */ - public static Observable filter(Observable that, Func1 predicate) { - return create(OperationFilter.filter(that, predicate)); - } - - /** - * Filters an Observable by discarding any items it emits that do not satisfy some predicate - *

- * - * - * @param that - * the Observable to filter - * @param function - * a function that evaluates an item emitted by the source Observable, and - * returns {@code true} if it passes the filter - * @return an Observable that emits only those items emitted by the source Observable for which the - * predicate function evaluates to {@code true} - */ - public static Observable filter(Observable that, final Object function) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(function); - return filter(that, new Func1() { - - @Override - public Boolean call(T t1) { - return (Boolean) _f.call(t1); - - } - - }); - } - - /** - * Filters an Observable by discarding any items it emits that do not satisfy some predicate - *

- * - * - * @param that - * the Observable to filter - * @param predicate - * a function that evaluates an item emitted by the source Observable, and - * returns {@code true} if it passes the filter - * @return an Observable that emits only those items emitted by the source Observable for which - * the predicate evaluates to {@code true} - */ - public static Observable where(Observable that, Func1 predicate) { - return create(OperationWhere.where(that, predicate)); - } - - /** - * Converts an {@link Iterable} sequence into an Observable. - *

- * - * - *

Implementation note: the entire iterable sequence will be immediately emitted each time an - * {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param iterable - * the source {@link Iterable} sequence - * @param - * the type of items in the {@link Iterable} sequence and the type of items to be - * emitted by the resulting Observable - * @return an Observable that emits each item in the source {@link Iterable} sequence - * @see #toObservable(Iterable) - */ - public static Observable from(Iterable iterable) { - return toObservable(iterable); - } - - /** - * Converts an Array into an Observable. - *

- * - * - *

Implementation note: the entire array will be immediately emitted each time an - * {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param items - * the source Array - * @param - * the type of items in the Array, and the type of items to be emitted by the - * resulting Observable - * @return an Observable that emits each item in the source Array - * @see #toObservable(Object...) - */ - public static Observable from(T... items) { - return toObservable(items); + public static Observable from(T... items) { + return create(OperationToObservableIterable.toObservableIterable(Arrays.asList(items))); } /** * Generates an Observable that emits a sequence of integers within a specified range. *

* - * - *

Implementation note: the entire range will be immediately emitted each time an - * {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, + * + *

Implementation note: the entire range will be immediately emitted each time an {@link Observer} subscribes. Since this occurs before the {@link Subscription} is returned, * it in not possible to unsubscribe from the sequence before it completes. - * + * * @param start * the value of the first integer in the sequence * @param count * the number of sequential integers to generate - * + * * @return an Observable that emits a range of sequential integers - * + * * @see Observable.Range Method (Int32, Int32) */ public static Observable range(int start, int count) { return from(Range.createWithCount(start, count)); } - /** - * Asynchronously subscribes and unsubscribes Observers on the specified {@link Scheduler}. - *

- * - * - * @param source - * the source Observable - * @param scheduler - * the {@link Scheduler} to perform subscription and unsubscription actions on - * @param - * the type of the items emitted by the Observable - * @return the source Observable modified so that its subscriptions and unsubscriptions happen - * on the specified {@link Scheduler} - */ - public static Observable subscribeOn(Observable source, Scheduler scheduler) { - return create(OperationSubscribeOn.subscribeOn(source, scheduler)); - } - - /** - * Asynchronously notify Observers on the specified {@link Scheduler}. - *

- * - * - * @param source - * the source Observable - * @param scheduler - * the {@link Scheduler} to notify Observers on - * @param - * the type of the items emitted by the Observable - * @return the source Observable modified so that its Observers are notified on the specified - * {@link Scheduler} - */ - public static Observable observeOn(Observable source, Scheduler scheduler) { - return create(OperationObserveOn.observeOn(source, scheduler)); - } - /** * Returns an Observable that calls an Observable factory to create its Observable for each * new Observer that subscribes. That is, for each subscriber, the actuall Observable is determined * by the factory function. - * + * *

* *

* The defer operator allows you to defer or delay emitting items from an Observable until such * time as an Observer subscribes to the Observable. This allows an {@link Observer} to easily * obtain updates or a refreshed version of the sequence. - * + * * @param observableFactory * the Observable factory function to invoke for each {@link Observer} that * subscribes to the resulting Observable @@ -1147,42 +582,9 @@ public static Observable defer(Func0> observableFactory) { } /** - * Returns an Observable that calls an Observable factory to create its Observable for each - * new Observer that subscribes. + * Returns an Observable that emits a single item and then completes. *

- * - *

- * The defer operator allows you to defer or delay emitting items from an Observable - * until such time as an {@link Observer} subscribes to the Observable. This allows an Observer - * to easily obtain an updates or refreshed version of the sequence. - * - * @param observableFactory - * the Observable factory function to invoke for each {@link Observer} that - * subscribes to the resulting Observable - * @param - * the type of the items emitted by the Observable - * @return an Observable whose {@link Observer}s trigger an invocation of the given Observable - * factory function - */ - public static Observable defer(Object observableFactory) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(observableFactory); - - return create(OperationDefer.defer(new Func0>() { - - @Override - @SuppressWarnings("unchecked") - public Observable call() { - return (Observable) _f.call(); - } - - })); - } - - /** - * Returns an Observable that emits a single item and then completes. - *

- * + * *

* To convert any object into an Observable that emits that object, pass that object into the * just method. @@ -1191,1339 +593,243 @@ public Observable call() { * 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 just() method * converts an Iterable into an Observable that emits the entire Iterable as a single item. - * + * * @param value * the item to pass to the {@link Observer}'s {@link Observer#onNext onNext} method * @param - * the type of that item - * @return an Observable that emits a single item and then completes - */ - public static Observable just(T value) { - List list = new ArrayList(); - list.add(value); - - return toObservable(list); - } - - /** - * Returns an Observable that applies a function of your choosing to each item emitted by an - * Observable and emits the result. - *

- * - * - * @param sequence - * the source Observable - * @param func - * a function to apply to each item emitted by the source Observable - * @param - * the type of items emitted by the the source Observable - * @param - * the type of items to be emitted by the resulting Observable - * @return an Observable that emits the items from the source Observable as transformed by the - * given function - */ - public static Observable map(Observable sequence, Func1 func) { - return create(OperationMap.map(sequence, func)); - } - - /** - * Returns an Observable that applies the given function to each item emitted by an - * Observable and emits the result. - *

- * - * - * @param sequence - * the source Observable - * @param func - * a function to apply to each item emitted by the source Observable - * @param - * the type of items emitted by the the source Observable - * @param - * the type of items to be emitted by the resulting Observable - * @return an Observable that emits the items from the source Observable as transformed by the - * given function - */ - public static Observable map(Observable sequence, final Object func) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(func); - return map(sequence, new Func1() { - - @SuppressWarnings("unchecked") - @Override - public R call(T t1) { - return (R) _f.call(t1); - } - - }); - } - - /** - * Creates a new Observable by applying a function that you supply to each item emitted by - * the source Observable, where that function returns an Observable, and then merging those - * resulting Observables and emitting the results of this merger. - *

- * - *

- * Note: {@code mapMany} and {@code flatMap} are equivalent. - * - * @param sequence - * the source Observable - * @param func - * a function that, when applied to an item emitted by the source Observable, - * returns an Observable - * @param - * the type of items emitted by the source Observable - * @param - * the type of items emitted by the Observables that are returned from - * {@code func} - * @return an Observable that emits the result of applying the transformation function to each - * item emitted by the source Observable and merging the results of the Observables - * obtained from this transformation - * @see #flatMap(Observable, Func1) - */ - public static Observable mapMany(Observable sequence, Func1> func) { - return create(OperationMap.mapMany(sequence, func)); - } - - /** - * Creates a new Observable by applying a function that you supply to each item emitted by - * the source Observable, where that function returns an Observable, and then merging those - * resulting Observables and emitting the results of this merger. - *

- * - *

- * Note: {@code mapMany} and {@code flatMap} are equivalent. - * - * @param sequence - * the source Observable - * @param func - * a function that, when applied to each item emitted by the source Observable, - * generates an Observable - * @param - * the type of items emitted by the source Observable - * @param - * the type of items emitted by the Observables that are returned from - * {@code func} - * @return an Observable that emits the result of applying the transformation function to each - * item emitted by the source Observable and merging the results of the Observables - * obtained from this transformation - */ - public static Observable mapMany(Observable sequence, final Object func) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(func); - return mapMany(sequence, new Func1() { - - @SuppressWarnings("unchecked") - @Override - public R call(T t1) { - return (R) _f.call(t1); - } - - }); - } - - /** - * Turns all of the notifications from a source Observable into {@link Observer#onNext onNext} - * emissions, and marks them with their original notification types within {@link Notification} - * objects. - *

- * - * - * @param sequence - * the Observable you want to materialize in this way - * @return an Observable that emits items that are the result of materializing the - * notifications of the source Observable. - * @see MSDN: Observable.Materialize - */ - public static Observable> materialize(final Observable sequence) { - return create(OperationMaterialize.materialize(sequence)); - } - - /** - * Reverses the effect of {@link #materialize materialize} by transforming the - * {@link Notification} objects emitted by a source Observable into the items or notifications - * they represent. - *

- * - * - * @param sequence - * an Observable that emits {@link Notification} objects that represent the items and - * notifications emitted by an Observable - * @return an Observable that emits the items and notifications embedded in the - * {@link Notification} objects emitted by the source Observable - * @see MSDN: Observable.Dematerialize - */ - public static Observable dematerialize(final Observable> sequence) { - return create(OperationDematerialize.dematerialize(sequence)); - } - - /** - * Flattens a list of Observables into one Observable, without any transformation. - *

- * - *

- * You can combine the items emitted by multiple Observables so that they act like a single - * Observable, by using the merge method. - * - * @param source - * a list of Observables - * @return an Observable that emits items that are the result of flattening the - * {@code source} list of Observables - * @see MSDN: Observable.Merge - */ - public static Observable merge(List> source) { - return create(OperationMerge.merge(source)); - } - - /** - * Flattens a sequence of Observables emitted by an Observable into one Observable, without any - * transformation. - *

- * - *

- * You can combine the items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param source - * an Observable that emits Observables - * @return an Observable that emits items that are the result of flattening the items emitted - * by the Observables emitted by the {@code source} Observable - * @see MSDN: Observable.Merge Method - */ - public static Observable merge(Observable> source) { - return create(OperationMerge.merge(source)); - } - - /** - * Flattens a series of Observables into one Observable, without any transformation. - *

- * - *

- * You can combine items emitted by multiple Observables so that they act like a single - * Observable, by using the {@code merge} method. - * - * @param source - * a series of Observables - * @return an Observable that emits items that are the result of flattening the items emitted - * by the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - public static Observable merge(Observable... source) { - return create(OperationMerge.merge(source)); - } - - /** - * Returns an Observable that emits the items from the {@code source} Observable until - * the {@code other} Observable emits an item. - *

- * - * - * @param source - * the source Observable - * @param other - * the Observable whose first emitted item will cause {@code takeUntil} to stop - * emitting items from the {@code source} Observable - * @param - * the type of items emitted by {@code source} - * @param - * the type of items emitted by {@code other} - * @return an Observable that emits the items emitted by {@code source} until such time as - * {@code other} emits its first item - */ - public static Observable takeUntil(final Observable source, final Observable other) { - return OperationTakeUntil.takeUntil(source, other); - } - - /** - * Returns an Observable that emits the items emitted by two or more Observables, one after the - * other. - *

- * - * - * @param source - * a series of Observables - * @return an Observable that emits items that are the result of combining the items emitted by - * the {@code source} Observables, one after the other - * @see MSDN: Observable.Concat Method - */ - public static Observable concat(Observable... source) { - return create(OperationConcat.concat(source)); - } - - /** - * Returns an Observable that emits the same items as the source Observable, and then calls - * the given Action after the Observable completes. - *

- * - * - * @param source - * an Observable - * @param action - * an {@link Action0} to be invoked when the source Observable completes - * or errors - * @return an Observable that emits the same items as the source, then invokes the action - * @see MSDN: - * Observable.Finally Method - */ - public static Observable finallyDo(Observable source, Action0 action) { - return create(OperationFinally.finallyDo(source, action)); - } - - /** - * Creates a new Observable by applying a function that you supply to each item emitted by - * the source Observable, where that function returns an Observable, and then merging those - * resulting Observables and emitting the results of this merger. - *

- * - *

- * Note: {@code mapMany} and {@code flatMap} are equivalent. - * - * @param sequence - * the source Observable - * @param func - * a function that, when applied to each item emitted by the source Observable, - * generates an Observable - * @param - * the type of items emitted by the source Observable - * @param - * the type of items emitted by the Observables that are returned from - * {@code func} - * @return an Observable that emits the result of applying the transformation function to each - * item emitted by the source Observable and merging the results of the Observables - * obtained from this transformation - * @see #mapMany(Observable, Func1) - */ - public static Observable flatMap(Observable sequence, Func1> func) { - return mapMany(sequence, func); - } - - /** - * Creates a new Observable by applying a function that you supply to each item emitted by - * the source Observable, where that function returns an Observable, and then merging those - * resulting Observables and emitting the results of this merger. - *

- * - *

- * Note: {@code mapMany} and {@code flatMap} are equivalent. - * - * @param sequence - * the source Observable - * @param func - * a function that, when applied to each item emitted by the source Observable, - * generates an Observable - * @param - * the type of items emitted by the source Observable - * @param - * the type of items emitted by the Observables that are returned from - * {@code func} - * @return an Observable that emits the result of applying the transformation function to each - * item emitted by the source Observable and merging the results of the Observables - * obtained from this transformation - * @see #mapMany(Observable, Func1) - */ - public static Observable flatMap(Observable sequence, final Object func) { - return mapMany(sequence, func); - } - - /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. - *

- * - * - * @param source - * an Observable whose items you want to group - * @param keySelector - * a function that extracts the key for each item omitted by the source Observable - * @param elementSelector - * a function to map each item emitted by the source Observable to an item emitted - * by a {@link GroupedObservable} - * @param - * the key type - * @param - * the type of items emitted by the source Observable - * @param - * the type of items to be emitted by the resulting {@link GroupedObservable}s - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value - */ - public static Observable> groupBy(Observable source, final Func1 keySelector, final Func1 elementSelector) { - return create(OperationGroupBy.groupBy(source, keySelector, elementSelector)); - } - - /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. - *

- * - * - * @param source - * an Observable whose items you want to group - * @param keySelector - * a function that extracts the key for each item omitted by the source Observable - * @param elementSelector - * a function to map each item emitted by the source Observable to an item emitted - * by a {@link GroupedObservable} - * @param - * the key type - * @param - * the type of items emitted by the source Observable - * @param - * the type of items to be emitted by the resulting {@link GroupedObservable}s - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value - */ - @SuppressWarnings("rawtypes") - public static Observable> groupBy(Observable source, final Object keySelector, final Object elementSelector) { - final FuncN _k = Functions.from(keySelector); - final FuncN _e = Functions.from(elementSelector); - - return groupBy(source, new Func1() { - - @SuppressWarnings("unchecked") - @Override - public K call(T t1) { - return (K) _k.call(t1); - } - }, new Func1() { - - @SuppressWarnings("unchecked") - @Override - public R call(T t1) { - return (R) _e.call(t1); - } - }); - } - - /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. - *

- * - * - * @param source - * an Observable whose items you want to group - * @param keySelector - * a function that extracts the key for each item emitted by the source Observable - * @param - * the key type - * @param - * the type of items to be emitted by the resulting {@link GroupedObservable}s - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value - */ - public static Observable> groupBy(Observable source, final Func1 keySelector) { - return create(OperationGroupBy.groupBy(source, keySelector)); - } - - /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. - *

- * - * - * @param source - * an Observable whose items you want to group - * @param keySelector - * a function that extracts the key for each item emitted by the source Observable - * @param - * the key type - * @param - * the type of items to be emitted by the resulting {@link GroupedObservable}s - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value - */ - @SuppressWarnings("rawtypes") - public static Observable> groupBy(Observable source, final Object keySelector) { - final FuncN _k = Functions.from(keySelector); - - return groupBy(source, new Func1() { - - @SuppressWarnings("unchecked") - @Override - public K call(T t1) { - return (K) _k.call(t1); - } - }); - } - - /** - * This behaves like {@link #merge(java.util.List)} 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. - *

- * 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. - * - * @param source - * a list of Observables - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} list of Observables - * @see MSDN: Observable.Merge Method - */ - public static Observable mergeDelayError(List> source) { - return create(OperationMergeDelayError.mergeDelayError(source)); - } - - /** - * This behaves like {@link #merge(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. - *

- * 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. - * - * @param source - * an Observable that emits Observables - * @return an Observable that emits items that are the result of flattening the items emitted by - * the Observables emitted by the {@code source} Observable - * @see MSDN: Observable.Merge Method - */ - public static Observable mergeDelayError(Observable> source) { - return create(OperationMergeDelayError.mergeDelayError(source)); - } - - /** - * This behaves like {@link #merge(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. - *

- * 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. - * - * @param source - * a series of Observables - * @return an Observable that emits items that are the result of flattening the items emitted by - * the {@code source} Observables - * @see MSDN: Observable.Merge Method - */ - public static Observable mergeDelayError(Observable... source) { - return create(OperationMergeDelayError.mergeDelayError(source)); - } - - /** - * Returns an Observable that never sends any items or notifications to an {@link Observer}. - *

- * - *

- * This Observable is useful primarily for testing purposes. - * - * @param - * the type of items (not) emitted by the Observable - * @return an Observable that never sends any items or notifications to an {@link Observer} - */ - public static Observable never() { - return new NeverObservable(); - } - - /** - * Instruct an Observable to pass control to another Observable (the return value of a function) - * 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 Observer, the Observable invokes its {@link 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 source Observable encounters an error, instead of - * invoking its Observer's {@code onError} function, it will instead relinquish control to - * this new Observable, 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. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param that - * the source Observable - * @param resumeFunction - * a function that returns an Observable that will take over if the source Observable - * encounters an error - * @return an Observable, identical to the source Observable with its behavior modified as described - */ - public static Observable onErrorResumeNext(final Observable that, final Func1> resumeFunction) { - return create(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(that, resumeFunction)); - } - - /** - * Instruct an Observable to pass control to another Observable (the return value of a function) - * 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 Observer, the Observable invokes its {@link 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 source Observable encounters an error, instead of - * invoking its Observer's {@code onError} function, it will instead relinquish control to - * this new Observable, 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. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param that - * the source Observable - * @param resumeFunction - * a function that returns an Observable that will take over if the source Observable - * encounters an error - * @return an Observable, identical to the source Observable with its behavior modified as described - */ - public static Observable onErrorResumeNext(final Observable that, final Object resumeFunction) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(resumeFunction); - return onErrorResumeNext(that, new Func1>() { - - @SuppressWarnings("unchecked") - @Override - public Observable call(Throwable e) { - return (Observable) _f.call(e); - } - }); - } - - /** - * 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 Observer, the Observable invokes its {@link 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 an - * Observable ({@code resumeSequence}) to {@code onErrorResumeNext}, if the original - * Observable encounters an error, instead of invoking its Observer's onError - * method, it will instead relinquish control to this new Observable, 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. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param that - * the source Observable - * @param resumeSequence - * a Observable that will take over if the source Observable encounters an error - * @return an Observable, identical to the source Observable with its behavior modified as described - */ - public static Observable onErrorResumeNext(final Observable that, final Observable resumeSequence) { - return create(OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable(that, resumeSequence)); - } - - /** - * Instruct an Observable to emit a particular item to its Observer's {@code onNext} - * function 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 {@code onErrorReturn} method changes this behavior. If you pass a function - * ({@code resumeFunction}) to {@code onErrorReturn}, if the source Observable - * encounters an error, instead of invoking its Observer's {@code onError} method, it will - * instead pass the return value of {@code resumeFunction} to the Observer's - * {@link Observer#onNext onNext} method. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param that - * the source Observable - * @param resumeFunction - * a function that returns an item that will be passed into an {@link Observer}'s - * {@link Observer#onNext onNext} method if the Observable encounters an error that - * would otherwise cause it to invoke {@link Observer#onError onError} - * @return an Observable, identical to the source Observable with its behavior modified as described - */ - public static Observable onErrorReturn(final Observable that, Func1 resumeFunction) { - return create(OperationOnErrorReturn.onErrorReturn(that, resumeFunction)); - } - - /** - * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error of type {@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. - *

- * - *

- * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its Observer, the Observable invokes its {@link 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 an - * Observable ({@code resumeSequence}) to {@code onErrorResumeNext}, if the original - * Observable encounters an error, instead of invoking its Observer's onError - * method, it will instead relinquish control to this new Observable, 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. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param that - * the source Observable - * @param resumeSequence - * a Observable that will take over if the source Observable encounters an error - * @return an Observable, identical to the source Observable with its behavior modified as described - */ - public static Observable onExceptionResumeNext(final Observable that, final Observable resumeSequence) { - return create(OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable(that, resumeSequence)); - } - - /** - * 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}. - *

- * - * @param that - * the source Observable - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * emit items to its {@link Observer}s - */ - public static ConnectableObservable replay(final Observable that) { - return OperationMulticast.multicast(that, ReplaySubject. create()); - } - - /** - * This method has similar behavior to {@link #replay} except that this auto-subscribes to - * the source Observable rather than returning a {@link ConnectableObservable}. - *

- * - *

- * This is useful when you want an Observable to cache responses and you can't control the - * subscribe/unsubscribe behavior of all the {@link Observer}s. - *

- * NOTE: You sacrifice the ability to unsubscribe from the origin when you use the - * 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 the items and - * notifications it emits so it can replay them for subsequent subscribers. - */ - public static Observable cache(final Observable that) { - return create(OperationCache.cache(that)); - } - - /** - * Returns a {@link ConnectableObservable}, which waits until its - * {@link ConnectableObservable#connect} method is called before it begins emitting items to - * those {@link Observer}s that have subscribed to it. - *

- * - * - * @param that - * the source Observable - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * emit items to its {@link Observer} - */ - public static ConnectableObservable publish(final Observable that) { - return OperationMulticast.multicast(that, PublishSubject. create()); - } - - /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by the source Observable into the same function, and so on until all items have been emitted - * by the source Observable, and emits the final result from the final call to your function as - * its sole item. - *

- * - *

- * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," - * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, - * has an {@code inject} method that does a similar operation on lists. - * - * @param - * the type of item emitted by the source Observable - * @param sequence - * the source Observable - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, the result of which will be used in the next accumulator call - * @return an Observable that emits a single item that is the result of applying the - * accumulator function to the sequence of items emitted by the source Observable - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public static Observable reduce(Observable sequence, Func2 accumulator) { - return takeLast(create(OperationScan.scan(sequence, accumulator)), 1); - } - - /** - * A version of {@code reduce()} for use by dynamic languages. - *

- * - * - * @see #reduce(Observable, Func2) - */ - public static Observable reduce(final Observable sequence, final Object accumulator) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(accumulator); - return reduce(sequence, new Func2() { - - @SuppressWarnings("unchecked") - @Override - public T call(T t1, T t2) { - return (T) _f.call(t1, t2); - } - - }); - } - - /** - * Synonymous with {@code reduce()} - *

- * - * - * @see #reduce(Observable, Func2) - */ - public static Observable aggregate(Observable sequence, Func2 accumulator) { - return reduce(sequence, accumulator); - } - - /** - * A version of {@code aggregate()} for use by dynamic languages. - *

- * - * - * @see #reduce(Observable, Func2) - */ - public static Observable aggregate(Observable sequence, Object accumulator) { - return reduce(sequence, accumulator); - } - - /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by the source Observable into the same function, and so on until all items have been emitted - * by the source Observable, emitting the final result from the final call to your function as - * its sole item. - *

- * - *

- * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," - * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, - * has an {@code inject} method that does a similar operation on lists. - * - * @param - * the type of item emitted by the source Observable - * @param - * the type returned by the accumulator function, and the type of the seed - * @param sequence - * the source Observable - * @param initialValue - * a seed to pass in to the first execution of the accumulator function - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, the result of which will be used in the next accumulator call - * @return an Observable that emits a single item that is the result of applying the - * accumulator function to the sequence of items emitted by the source Observable - * @see MSDN: Observable.Aggregate - * @see Wikipedia: Fold (higher-order function) - */ - public static Observable reduce(Observable sequence, R initialValue, Func2 accumulator) { - return takeLast(create(OperationScan.scan(sequence, initialValue, accumulator)), 1); - } - - /** - * A version of {@code reduce()} for use by dynamic languages. - *

- * - * - * @see #reduce(Observable, Object, Func2) - */ - public static Observable reduce(final Observable sequence, final R initialValue, final Object accumulator) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(accumulator); - return reduce(sequence, initialValue, new Func2() { - @SuppressWarnings("unchecked") - @Override - public R call(R r, T t) { - return (R) _f.call(r, t); - } - }); - } - - /** - * Synonymous with {@code reduce()}. - *

- * - * - * @see #reduce(Observable, Object, Func2) - */ - public static Observable aggregate(Observable sequence, R initialValue, Func2 accumulator) { - return reduce(sequence, initialValue, accumulator); - } - - /** - * A version of {@code aggregate()} for use by dynamic languages. - *

- * - * - * @see #reduce(Observable, Object, Func2) - */ - public static Observable aggregate(Observable sequence, R initialValue, Object accumulator) { - return reduce(sequence, initialValue, accumulator); - } - - /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by the source Observable into the same function, and so on until all items have been emitted - * by the source Observable, emitting the result of each of these iterations. - *

- * - * - * @param - * the type of item emitted by the source Observable - * @param sequence - * the source Observable - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, the result of which will be emitted and used in the next accumulator - * call - * @return an Observable that emits items that are the result of accumulating the items from - * the source Observable - * @see MSDN: Observable.Scan - */ - public static Observable scan(Observable sequence, Func2 accumulator) { - return create(OperationScan.scan(sequence, accumulator)); - } - - /** - * A version of {@code scan()} for use by dynamic languages. - *

- * - * - * @see #scan(Observable, Func2) - */ - public static Observable scan(final Observable sequence, final Object accumulator) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(accumulator); - return scan(sequence, new Func2() { - - @SuppressWarnings("unchecked") - @Override - public T call(T t1, T t2) { - return (T) _f.call(t1, t2); - } - - }); - } - - /** - * Returns an Observable that applies a function of your choosing to the first item emitted by a - * source Observable, then feeds the result of that function along with the second item emitted - * by the source Observable into the same function, and so on until all items have been emitted - * by the source Observable, emitting the result of each of these iterations. - *

- * - *

- * Note that when you pass a seed to {@code scan()}, that seed will be the first item - * emitted by the resulting Observable. - * - * @param - * the type of item emitted by the source Observable - * @param - * the type returned by the accumulator function, and the type of the seed - * @param sequence - * the source Observable - * @param initialValue - * the initial (seed) accumulator value - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, the result of which will be emitted and used in the next accumulator - * call - * @return an Observable that emits items that are the result of accumulating the items emitted - * by the source Observable - * @see MSDN: Observable.Scan - */ - public static Observable scan(Observable sequence, R initialValue, Func2 accumulator) { - return create(OperationScan.scan(sequence, initialValue, accumulator)); - } - - /** - * A version of {@code scan()} for use by dynamic languages. - *

- * - * - * @see #scan(Observable, Object, Func2) - */ - public static Observable scan(final Observable sequence, final R initialValue, final Object accumulator) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(accumulator); - return scan(sequence, initialValue, new Func2() { - - @SuppressWarnings("unchecked") - @Override - public R call(R r, T t) { - return (R) _f.call(r, t); - } - }); - } - - /** - * Returns an Observable that emits a single Boolean value that indicates whether all items emitted by a - * source Observable satisfy a condition. - *

- * - * - * @param sequence - * an Observable whose emitted items you are evaluating - * @param predicate - * a function that evaluates each emitted item and returns a Boolean - * @param - * the type of items emitted by the source Observable - * @return an Observable that emits {@code true} if all of the items emitted by the source - * Observable satisfy the predicate; otherwise, {@code false} - */ - public static Observable all(final Observable sequence, final Func1 predicate) { - return create(OperationAll.all(sequence, predicate)); - } - - /** - * Returns an Observable that emits a single Boolean value that indicates whether all items emitted by a - * source Observable satisfy a condition. - *

- * - * - * @param sequence - * an Observable whose emitted items you are evaluating - * @param predicate - * a function that evaluates each emitted item and returns a Boolean - * @param - * the type of items emitted by the source Observable - * @return an Observable that emits {@code true} if all items emitted by the source - * Observable satisfy the predicate; otherwise, {@code false} - */ - public static Observable all(final Observable sequence, Object predicate) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(predicate); - - return all(sequence, new Func1() { - @Override - public Boolean call(T t) { - return (Boolean) _f.call(t); - } - }); - } - - /** - * Returns an Observable that skips the first {@code num} items emitted by the source - * Observable and emits the remaining items. - *

- * - *

- * You can ignore the first {@code num} items emitted by an Observable and attend only to - * those items that come after, by modifying the Observable with the {@code skip} method. - * - * @param items - * the source Observable - * @param num - * the number of items to skip - * @return an Observable that emits the same items emitted by the source Observable, except for - * the first {@code num} items - * @see MSDN: Observable.Skip Method + * the type of that item + * @return an Observable that emits a single item and then completes */ - public static Observable skip(final Observable items, int num) { - return create(OperationSkip.skip(items, num)); - } + public static Observable just(T value) { + List list = new ArrayList(); + list.add(value); - /** - * Given an Observable that emits Observables, creates a single Observable that - * emits the items emitted by the most recently published of those Observables. - *

- * - * - * @param sequenceOfSequences - * the source Observable that emits Observables - * @return an Observable that emits only the items emitted by the most recently published - * Observable - */ - public static Observable switchDo(Observable> sequenceOfSequences) { - return create(OperationSwitch.switchDo(sequenceOfSequences)); + return from(list); } /** - * Accepts an Observable and wraps it in another Observable that ensures that the resulting - * Observable is chronologically well-behaved. + * Flattens a list of Observables into one Observable, without any transformation. *

- * + * *

- * 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 - * {@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. - * - * @param observable - * the source Observable - * @param - * the type of item emitted by the source Observable - * @return an Observable that is a chronologically well-behaved version of the source - * Observable, and that synchronously notifies its {@link Observer}s + * You can combine the items emitted by multiple Observables so that they act like a single + * Observable, by using the merge method. + * + * @param source + * a list of Observables + * @return an Observable that emits items that are the result of flattening the {@code source} list of Observables + * @see MSDN: Observable.Merge */ - public static Observable synchronize(Observable observable) { - return create(OperationSynchronize.synchronize(observable)); + public static Observable merge(List> source) { + return create(OperationMerge.merge(source)); } /** - * Returns an Observable that emits the first {@code num} items emitted by the source - * Observable. + * Flattens a sequence of Observables emitted by an Observable into one Observable, without any + * transformation. *

- * + * *

- * This method returns an Observable that will invoke a subscribing {@link Observer}'s - * {@link Observer#onNext onNext} method a maximum of {@code num} times before invoking - * {@link Observer#onCompleted onCompleted}. - * - * @param items - * the source Observable - * @param num - * the number of items to emit from the start of the sequence emitted by the source - * Observable - * @return an Observable that emits only the first {@code num} items emitted by the source - * Observable + * You can combine the items emitted by multiple Observables so that they act like a single + * Observable, by using the {@code merge} method. + * + * @param source + * an Observable that emits Observables + * @return an Observable that emits items that are the result of flattening the items emitted + * by the Observables emitted by the {@code source} Observable + * @see MSDN: Observable.Merge Method */ - public static Observable take(final Observable items, final int num) { - return create(OperationTake.take(items, num)); + public static Observable merge(Observable> source) { + return create(OperationMerge.merge(source)); } /** - * Returns an Observable that emits the last {@code count} items emitted by the source - * Observable. + * Flattens a series of Observables into one Observable, without any transformation. *

- * - * - * @param items - * the source Observable - * @param count - * the number of items to emit from the end of the sequence emitted by the source - * Observable - * @return an Observable that emits only the last count items emitted by the source - * Observable + * + *

+ * You can combine items emitted by multiple Observables so that they act like a single + * Observable, by using the {@code merge} method. + * + * @param source + * a series of Observables + * @return an Observable that emits items that are the result of flattening the items emitted + * by the {@code source} Observables + * @see MSDN: Observable.Merge Method */ - public static Observable takeLast(final Observable items, final int count) { - return create(OperationTakeLast.takeLast(items, count)); + public static Observable merge(Observable... source) { + return create(OperationMerge.merge(source)); } /** - * Returns an Observable that emits the items emitted by a source Observable so long as a given - * predicate, operating on the items emitted, remains true. + * Returns an Observable that emits the items emitted by two or more Observables, one after the + * other. *

- * - * - * @param items - * the source Observable - * @param predicate - * a function to test each item emitted by the source Observable for a condition - * @return an Observable that emits items from the source Observable so long as the predicate - * continues to return {@code true} for each item, then completes + * + * + * @param source + * a series of Observables + * @return an Observable that emits items that are the result of combining the items emitted by + * the {@code source} Observables, one after the other + * @see MSDN: Observable.Concat Method */ - public static Observable takeWhile(final Observable items, Func1 predicate) { - return create(OperationTakeWhile.takeWhile(items, predicate)); + public static Observable concat(Observable... source) { + return create(OperationConcat.concat(source)); } /** - * Returns an Observable that emits the items emitted by a source Observable so long as a given - * predicate, operating on the items emitted, remains true. + * This behaves like {@link #merge(java.util.List)} 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. *

- * - * - * @param items - * the source Observable - * @param predicate - * a function to test each item emitted by the source Observable for a condition - * @return an Observable that emits items from the source Observable so long as the predicate - * continues to return {@code true} for each item, then completes + * + *

+ * 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. + * + * @param source + * a list of Observables + * @return an Observable that emits items that are the result of flattening the items emitted by + * the {@code source} list of Observables + * @see MSDN: Observable.Merge Method */ - public static Observable takeWhile(final Observable items, Object predicate) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(predicate); - - return takeWhile(items, new Func1() { - @Override - public Boolean call(T t) { - return (Boolean) _f.call(t); - } - }); + public static Observable mergeDelayError(List> source) { + return create(OperationMergeDelayError.mergeDelayError(source)); } /** - * Returns an Observable that emits the items emitted by a source Observable so long as a given - * predicate remains true, where the predicate can operate on both the item and its index - * relative to the complete sequence. + * This behaves like {@link #merge(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. *

- * - * - * @param items - * the source Observable - * @param predicate - * a function to test each item emitted by the source Observable for a condition; - * the second parameter of the function represents the index of the source item - * @return an Observable that emits items from the source Observable so long as the predicate - * continues to return {@code true} for each item, then completes + * + *

+ * 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. + * + * @param source + * an Observable that emits Observables + * @return an Observable that emits items that are the result of flattening the items emitted by + * the Observables emitted by the {@code source} Observable + * @see MSDN: Observable.Merge Method */ - public static Observable takeWhileWithIndex(final Observable items, Func2 predicate) { - return create(OperationTakeWhile.takeWhileWithIndex(items, predicate)); + public static Observable mergeDelayError(Observable> source) { + return create(OperationMergeDelayError.mergeDelayError(source)); } /** - * Returns an Observable that emits the items emitted by a source Observable so long as a given - * predicate remains true, where the predicate can operate on both the item and its index - * relative to the complete sequence. + * This behaves like {@link #merge(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. *

- * - * - * @param items - * the source Observable - * @param predicate - * a function to test each item emitted by the source Observable for a condition; - * the second parameter of the function represents the index of the source item - * @return an Observable that emits items from the source Observable so long as the predicate - * continues to return {@code true} for each item, then completes + * + *

+ * 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. + * + * @param source + * a series of Observables + * @return an Observable that emits items that are the result of flattening the items emitted by + * the {@code source} Observables + * @see MSDN: Observable.Merge Method */ - public static Observable takeWhileWithIndex(final Observable items, Object predicate) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(predicate); - - return create(OperationTakeWhile.takeWhileWithIndex(items, new Func2() - { - @Override - public Boolean call(T t, Integer integer) - { - return (Boolean) _f.call(t, integer); - } - })); + public static Observable mergeDelayError(Observable... source) { + return create(OperationMergeDelayError.mergeDelayError(source)); } /** - * Wraps each item emitted by a source Observable in a {@link Timestamped} object. + * Returns an Observable that never sends any items or notifications to an {@link Observer}. *

- * - * - * @return an Observable that emits timestamped items from the source Observable + * + *

+ * This Observable is useful primarily for testing purposes. + * + * @param + * the type of items (not) emitted by the Observable + * @return an Observable that never sends any items or notifications to an {@link Observer} */ - public Observable> timestamp() { - return create(OperationTimestamp.timestamp(this)); + public static Observable never() { + return new NeverObservable(); } /** - * Returns an Observable that emits a single item, a list composed of all the items emitted by - * the source Observable. - *

- * - *

- * Normally, an Observable that emits multiple items will do so by invoking its - * {@link Observer}'s {@link Observer#onNext onNext} method once for each such item. {@code toList} - * allows you can change this behavior, instructing the Observable to compose a List of all of the - * items and then invoke the Observer's {@code onNext} function once, passing the entire list. + * Given an Observable that emits Observables, creates a single Observable that + * emits the items emitted by the most recently published of those Observables. *

- * Be careful not to use this operator on Observables that emit an infinite or very large - * number of items, as all items will be held in memory and you do not have the option to - * unsubscribe. - * - * @param that - * the source Observable - * @return an Observable that emits a single item: a {@code List} containing all of the - * items emitted by the source Observable + * + * + * @param sequenceOfSequences + * the source Observable that emits Observables + * @return an Observable that emits only the items emitted by the most recently published + * Observable */ - public static Observable> toList(final Observable that) { - return create(OperationToObservableList.toObservableList(that)); + public static Observable switchDo(Observable> sequenceOfSequences) { + // TODO should this static remain? I have left it because it is an Observable + return create(OperationSwitch.switchDo(sequenceOfSequences)); } /** - * Returns a {@link ConnectableObservable} that upon connection causes the source Observable to - * emit items into the specified {@link Subject}. - * - * @param source - * the source Observable whose emitted items will be pushed into the specified - * {@link Subject} - * @param subject - * the {@link Subject} to push source items into - * @param - * the type of items emitted by the source Observable - * @param - * the type of the {@link Subject} - * @return a {@link ConnectableObservable} that upon connection causes the source Observable to - * push items into the specified {@link Subject} + * Given an Observable that emits Observables, creates a single Observable that + * emits the items emitted by the most recently published of those Observables. + *

+ * + * + * @param sequenceOfSequences + * the source Observable that emits Observables + * @return an Observable that emits only the items emitted by the most recently published + * Observable + * @throws ClassCastException + * if sequence not of type {@code Observable} */ - public static ConnectableObservable multicast(Observable source, final Subject subject) { - return OperationMulticast.multicast(source, subject); + @SuppressWarnings("unchecked") + public Observable switchDo() { + // TODO can we come up with a better name than this? It should be 'switch' but that is reserved. + // Perhaps 'switchOnNext'? + return create(OperationSwitch.switchDo((Observable>) this)); } /** - * Converts an {@link Iterable} sequence into an Observable. + * Accepts an Observable and wraps it in another Observable that ensures that the resulting + * Observable is chronologically well-behaved. *

- * + * *

- * You can convert any object that supports the Iterable interface into an Observable that - * emits each item in the Iterable, by passing the Iterable into the toObservable - * method. - * - * @param iterable - * the source {@link Iterable} sequence + * 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 {@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. + * + * @param observable + * the source Observable * @param - * the type of items in the {@link Iterable} sequence and the type of items to be - * emitted by the resulting Observable - * @return an Observable that emits each item in the source {@link Iterable} sequence + * the type of item emitted by the source Observable + * @return an Observable that is a chronologically well-behaved version of the source + * Observable, and that synchronously notifies its {@link Observer}s */ - public static Observable toObservable(Iterable iterable) { - return create(OperationToObservableIterable.toObservableIterable(iterable)); + public static Observable synchronize(Observable observable) { + return create(OperationSynchronize.synchronize(observable)); } /** - * Converts a {@link Future} into an Observable. - * - *

- * + * Wraps each item emitted by a source Observable in a {@link Timestamped} object. *

- * Important note: This Observable is blocking; you cannot unsubscribe from it. - * - * @param future - * the source {@link Future} - * @param - * the type of of object that the {@link Future} returns, and also the type of the - * item emitted by the resulting Observable - * @return an Observable that emits the item from the source {@link Future} - * @deprecated Replaced by {@link #from(Future)} + * + * + * @return an Observable that emits timestamped items from the source Observable */ - public static Observable toObservable(Future future) { - return create(OperationToObservableFuture.toObservableFuture(future)); + public Observable> timestamp() { + return create(OperationTimestamp.timestamp(this)); } /** @@ -2536,7 +842,7 @@ public static Observable toObservable(Future future) { * object into the {@code from} method. *

* Important note: This Observable is blocking; you cannot unsubscribe from it. - * + * * @param future * the source {@link Future} * @param @@ -2549,24 +855,26 @@ public static Observable from(Future future) { } /** - * Converts a {@link Future} into an Observable with timeout. + * Converts a {@link Future} into an Observable. *

- * Important note: This Observable is blocking; you cannot unsubscribe from it. - * + * + *

+ * You can convert any object that supports the {@link Future} interface into an Observable that + * emits the return value of the {@link Future#get} method of that object, by passing the + * object into the {@code from} method. + *

+ * * @param future * the source {@link Future} - * @param timeout - * the maximum time to wait - * @param unit - * the {@link TimeUnit} of the time argument + * @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. * @param * the type of object that the {@link Future} returns, and also the type of item to * be emitted by the resulting Observable - * @return an Observable that emits the item from the source {@link Future} - * @deprecated Replaced by {@link #from(Future, long, TimeUnit)} + * @return an Observable that emits the item from the source Future */ - public static Observable toObservable(Future future, long timeout, TimeUnit unit) { - return create(OperationToObservableFuture.toObservableFuture(future, timeout, unit)); + public static Observable from(Future future, Scheduler scheduler) { + return create(OperationToObservableFuture.toObservableFuture(future)).subscribeOn(scheduler); } /** @@ -2579,7 +887,7 @@ public static Observable toObservable(Future future, long timeout, Tim * object into the {@code from} method. *

* Important note: This Observable is blocking; you cannot unsubscribe from it. - * + * * @param future * the source {@link Future} * @param timeout @@ -2596,97 +904,15 @@ public static Observable from(Future future, long timeout, TimeUnit un } /** - * Converts an array sequence into an Observable. - * - * @param items - * the source array - * @param - * the type of items in the array, and also the type of items emitted by the - * resulting Observable - * @return an Observable that emits each item in the source array - * @deprecated Use {@link #from(Object...)} - */ - public static Observable toObservable(T... items) { - return toObservable(Arrays.asList(items)); - } - - /** - * Return an Observable that emits a single list of the items emitted by the source Observable, in sorted - * order (each item emitted by the source Observable must implement {@link Comparable} with - * respect to all other items emitted by the source Observable). - *

- * - * - * @param sequence - * the source Observable - * @throws ClassCastException - * if any emitted item does not implement {@link Comparable} with respect to all - * other emitted items - * @return an Observable that emits a single,sorted list of the items from the source Observable - */ - public static Observable> toSortedList(Observable sequence) { - return create(OperationToObservableSortedList.toSortedList(sequence)); - } - - /** - * Return an Observable that emits a single list of the items emitted by the source Observable, sorted - * by the given comparison function. - *

- * - * - * @param sequence - * the source Observable - * @param sortFunction - * a function that compares two items emitted by the source Observable and returns - * an Integer that indicates their sort order - * @return an Observable that emits a single, sorted list of the items from the source Observable - */ - public static Observable> toSortedList(Observable sequence, Func2 sortFunction) { - return create(OperationToObservableSortedList.toSortedList(sequence, sortFunction)); - } - - /** - * Return an Observable that emits a single list of the items emitted by the source Observable, sorted - * by the given comparison function. - *

- * - * - * @param sequence - * the source Observable - * @param sortFunction - * a function that compares two items emitted by the source Observable and returns - * an Integer that indicates their sort order - * @return an Observable that emits a single, sorted list of the items from the source Observable - */ - public static Observable> toSortedList(Observable sequence, final Object sortFunction) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(sortFunction); - return create(OperationToObservableSortedList.toSortedList(sequence, new Func2() { - - @Override - public Integer call(T t1, T t2) { - return (Integer) _f.call(t1, t2); - } - - })); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to pairs - * of 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 w0} and the first item emitted by {@code w1}; the second item emitted by + * the new Observable will be the result of the function applied to the second item emitted by {@code w0} and the second item emitted by {@code w1}; and so forth. *

- * {@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 w0} and the first item emitted by {@code w1}; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * {@code w0} and the second item emitted by {@code w1}; 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 + * 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 w0 * one source Observable * @param w1 @@ -2706,7 +932,7 @@ public static Observable zip(Observable w0, Observable w1 * emitted by two source Observables are equal. *

* - * + * * @param first * one Observable to compare * @param second @@ -2731,7 +957,7 @@ public Boolean call(T first, T second) { * function. *

* - * + * * @param first * one Observable to compare * @param second @@ -2747,85 +973,19 @@ public static Observable sequenceEqual(Observable first, Observa return zip(first, second, equality); } - /** - * Returns an Observable that emits Boolean values that indicate whether the pairs of items - * emitted by two source Observables are equal based on the results of a specified equality - * function. - *

- * - * - * @param first - * one Observable to compare - * @param second - * the second Observable to compare - * @param equality - * a function used to compare items emitted by both Observables - * @param - * the type of items emitted by each Observable - * @return an Observable that emits Booleans that indicate whether the corresponding items - * emitted by the source Observables are equal - */ - public static Observable sequenceEqual(Observable first, Observable second, Object equality) { - return zip(first, second, equality); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to pairs - * of items emitted, in sequence, by two other Observables. - *

- * - *

- * 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 - * w0 and the first item emitted by w1; the second item emitted by - * the new Observable will be the result of the function applied to the second item emitted by - * w0 and the second item emitted by w1; and so forth. - *

- * The resulting Observable<R> returned from zip will invoke - * {@link Observer#onNext onNext} as many times as the number of onNext invocations - * of the source Observable that emits the fewest items. - * - * @param w0 - * one source Observable - * @param w1 - * another source Observable - * @param function - * a function that, when applied to a pair of items, each emitted by one of the two - * source Observables, results in an item that will be emitted by the resulting - * Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable w0, Observable w1, final Object function) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(function); - return zip(w0, w1, new Func2() { - - @SuppressWarnings("unchecked") - @Override - public R call(T0 t0, T1 t1) { - return (R) _f.call(t0, t1); - } - - }); - } - /** * Returns an Observable that emits the results of a function of your choosing applied to * 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 w0}, the first item emitted by {@code w1}, and the first item emitted by {@code w2}; the second + * item emitted by the new Observable will be the result of the + * function applied to the second item emitted by {@code w0}, the second item emitted by {@code w1}, and the second item emitted by {@code w2}; and so forth. *

- * {@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 w0}, the first item emitted by {@code w1}, and the first item emitted by - * {@code w2}; the second item emitted by the new Observable will be the result of the - * function applied to the second item emitted by {@code w0}, the second item emitted by - * {@code w1}, and the second item emitted by {@code w2}; 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 + * 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 w0 * one source Observable * @param w1 @@ -2841,65 +1001,20 @@ public static Observable zip(Observable w0, Observable - * - *

- * 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 - * w0, the first item emitted by w1, and the first item emitted by - * w2; the second item emitted by the new Observable will be the result of the - * function applied to the second item emitted by w0, the second item emitted by - * w1, and the second item emitted by w2; and so forth. - *

- * The resulting Observable<R> returned from zip will invoke - * {@link Observer#onNext onNext} as many times as the number of onNext invocations - * of the source Observable that emits the fewest items. - * - * @param w0 - * one source Observable - * @param w1 - * another source Observable - * @param w2 - * a third source Observable - * @param function - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable w0, Observable w1, Observable w2, final Object function) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(function); - return zip(w0, w1, w2, new Func3() { - - @SuppressWarnings("unchecked") - @Override - public R call(T0 t0, T1 t1, T2 t2) { - return (R) _f.call(t0, t1, t2); - } - - }); - } - /** * Returns an Observable that emits the results of a function of your choosing applied to * 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 w0}, the first item emitted by {@code w1}, the first item emitted by - * {@code w2}, and the first item emitted by {@code w3}; the second item emitted by + *

{@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 w0}, the first item emitted by {@code w1}, the first item emitted by {@code w2}, and the first item + * emitted by {@code w3}; 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 + * 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 w0 * one source Observable * @param w1 @@ -2918,78 +1033,64 @@ public static Observable zip(Observable w0, Observabl } /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of four items emitted, in sequence, by four other Observables. - *

- * - *

- * 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 - * w0, the first item emitted by w1, the first item emitted by - * w2, and the first item emitted by w3; 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. + * Combines the given observables, emitting an event containing an aggregation of the latest values of each of the source observables + * each time an event is received from one of the source observables, where the aggregation is defined by the given function. *

- * The resulting Observable<R> returned from zip will invoke - * {@link Observer#onNext onNext} as many times as the number of onNext invocations - * of the source Observable that emits the fewest items. - * + * + * * @param w0 - * one source Observable + * The first source observable. * @param w1 - * another source Observable - * @param w2 - * a third source Observable - * @param w3 - * a fourth source Observable - * @param function - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results + * The second source observable. + * @param combineFunction + * The aggregation function used to combine the source observable values. + * @return An Observable that combines the source Observables with the given combine function */ - public static Observable zip(Observable w0, Observable w1, Observable w2, Observable w3, final Object function) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(function); - return zip(w0, w1, w2, w3, new Func4() { + public static Observable combineLatest(Observable w0, Observable w1, Func2 combineFunction) { + return create(OperationCombineLatest.combineLatest(w0, w1, combineFunction)); + } - @SuppressWarnings("unchecked") - @Override - public R call(T0 t0, T1 t1, T2 t2, T3 t3) { - return (R) _f.call(t0, t1, t2, t3); - } + /** + * @see #combineLatest(Observable, Observable, Func2) + */ + public static Observable combineLatest(Observable w0, Observable w1, Observable w2, Func3 combineFunction) { + return create(OperationCombineLatest.combineLatest(w0, w1, w2, combineFunction)); + } - }); + /** + * @see #combineLatest(Observable, Observable, Func2) + */ + public static Observable combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Func4 combineFunction) { + return create(OperationCombineLatest.combineLatest(w0, w1, w2, w3, combineFunction)); } /** * Creates an Observable which produces buffers of collected values. - * + * *

This Observable produces connected non-overlapping buffers. The current buffer is - * emitted and replaced with a new buffer when the Observable produced by the specified - * {@link Func0} produces a {@link BufferClosing} object. The * {@link Func0} will then + * emitted and replaced with a new buffer when the Observable produced by the specified {@link Func0} produces a {@link BufferClosing} object. The * {@link Func0} will then * be used to create a new Observable to listen for the end of the next buffer. - * + * * @param bufferClosingSelector * The {@link Func0} which is used to produce an {@link Observable} for every buffer created. * When this {@link Observable} produces a {@link BufferClosing} object, the associated buffer * is emitted and replaced with a new one. * @return - * An {@link Observable} which produces connected non-overlapping buffers, which are emitted - * when the current {@link Observable} created with the {@link Func0} argument produces a - * {@link BufferClosing} object. + * An {@link Observable} which produces connected non-overlapping buffers, which are emitted + * when the current {@link Observable} created with the {@link Func0} argument produces a {@link BufferClosing} object. */ public Observable> buffer(Func0> bufferClosingSelector) { - return buffer(this, bufferClosingSelector); + return create(OperationBuffer.buffer(this, bufferClosingSelector)); } /** * Creates an Observable which produces buffers of collected values. - * + * *

This Observable produces buffers. Buffers are created when the specified "bufferOpenings" * Observable produces a {@link BufferOpening} object. Additionally the {@link Func0} argument * is used to create an Observable which produces {@link BufferClosing} objects. When this * Observable produces such an object, the associated buffer is emitted. - * + * * @param bufferOpenings * The {@link Observable} which, when it produces a {@link BufferOpening} object, will cause * another buffer to be created. @@ -2998,76 +1099,75 @@ public Observable> buffer(Func0> bufferClosing * When this {@link Observable} produces a {@link BufferClosing} object, the associated buffer * is emitted. * @return - * An {@link Observable} which produces buffers which are created and emitted when the specified - * {@link Observable}s publish certain objects. + * An {@link Observable} which produces buffers which are created and emitted when the specified {@link Observable}s publish certain objects. */ public Observable> buffer(Observable bufferOpenings, Func1> bufferClosingSelector) { - return buffer(this, bufferOpenings, bufferClosingSelector); + return create(OperationBuffer.buffer(this, bufferOpenings, bufferClosingSelector)); } /** * Creates an Observable which produces buffers of collected values. - * + * *

This Observable produces connected non-overlapping buffers, each containing "count" * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. - * + * * @param count * The maximum size of each buffer before it should be emitted. * @return - * An {@link Observable} which produces connected non-overlapping buffers containing at most - * "count" produced values. + * An {@link Observable} which produces connected non-overlapping buffers containing at most + * "count" produced values. */ public Observable> buffer(int count) { - return buffer(this, count); + return create(OperationBuffer.buffer(this, count)); } /** * Creates an Observable which produces buffers of collected values. - * + * *

This Observable produces buffers every "skip" values, each containing "count" * elements. When the source Observable completes or encounters an error, the current * buffer is emitted, and the event is propagated. - * + * * @param count * The maximum size of each buffer before it should be emitted. * @param skip * How many produced values need to be skipped before starting a new buffer. Note that when "skip" and * "count" are equals that this is the same operation as {@link Observable#buffer(Observable, int)}. * @return - * An {@link Observable} which produces buffers every "skipped" values containing at most - * "count" produced values. + * An {@link Observable} which produces buffers every "skipped" values containing at most + * "count" produced values. */ public Observable> buffer(int count, int skip) { - return buffer(this, count, skip); + return create(OperationBuffer.buffer(this, count, skip)); } /** * Creates an Observable which produces buffers of collected values. - * + * *

This Observable produces connected non-overlapping buffers, each of a fixed duration * specified by the "timespan" argument. When the source Observable completes or encounters * an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. * @param unit * The unit of time which applies to the "timespan" argument. * @return - * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. + * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. */ public Observable> buffer(long timespan, TimeUnit unit) { - return buffer(this, timespan, unit); + return create(OperationBuffer.buffer(this, timespan, unit)); } /** * Creates an Observable which produces buffers of collected values. - * + * *

This Observable produces connected non-overlapping buffers, each of a fixed duration * specified by the "timespan" argument. When the source Observable completes or encounters * an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. @@ -3076,10 +1176,10 @@ public Observable> buffer(long timespan, TimeUnit unit) { * @param scheduler * The {@link Scheduler} to use when determining the end and start of a buffer. * @return - * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. + * An {@link Observable} which produces connected non-overlapping buffers with a fixed duration. */ public Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) { - return buffer(this, timespan, unit, scheduler); + return create(OperationBuffer.buffer(this, timespan, unit, scheduler)); } /** @@ -3087,7 +1187,7 @@ public Observable> buffer(long timespan, TimeUnit unit, Scheduler schedu * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size * specified by the "count" argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. @@ -3096,11 +1196,11 @@ public Observable> buffer(long timespan, TimeUnit unit, Scheduler schedu * @param count * The maximum size of each buffer before it should be emitted. * @return - * An {@link Observable} which produces connected non-overlapping buffers which are emitted after - * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). + * An {@link Observable} which produces connected non-overlapping buffers which are emitted after + * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). */ public Observable> buffer(long timespan, TimeUnit unit, int count) { - return buffer(this, timespan, unit, count); + return create(OperationBuffer.buffer(this, timespan, unit, count)); } /** @@ -3108,7 +1208,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count) { * non-overlapping buffers, each of a fixed duration specified by the "timespan" argument or a maximum size * specified by the "count" argument (which ever is reached first). When the source Observable completes * or encounters an error, the current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted, and * replaced with a new buffer. @@ -3119,11 +1219,11 @@ public Observable> buffer(long timespan, TimeUnit unit, int count) { * @param scheduler * The {@link Scheduler} to use when determining the end and start of a buffer. * @return - * An {@link Observable} which produces connected non-overlapping buffers which are emitted after - * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). + * An {@link Observable} which produces connected non-overlapping buffers which are emitted after + * a fixed duration or when the buffer has reached maximum capacity (which ever occurs first). */ public Observable> buffer(long timespan, TimeUnit unit, int count, Scheduler scheduler) { - return buffer(this, timespan, unit, count, scheduler); + return create(OperationBuffer.buffer(this, timespan, unit, count, scheduler)); } /** @@ -3131,7 +1231,7 @@ public Observable> buffer(long timespan, TimeUnit unit, int count, Sched * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan * specified by the "timespan" argument. When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted. * @param timeshift @@ -3139,11 +1239,11 @@ public Observable> buffer(long timespan, TimeUnit unit, int count, Sched * @param unit * The unit of time which applies to the "timespan" and "timeshift" argument. * @return - * An {@link Observable} which produces new buffers periodically, and these are emitted after - * a fixed timespan has elapsed. + * An {@link Observable} which produces new buffers periodically, and these are emitted after + * a fixed timespan has elapsed. */ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) { - return buffer(this, timespan, timeshift, unit); + return create(OperationBuffer.buffer(this, timespan, timeshift, unit)); } /** @@ -3151,7 +1251,7 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) * periodically, which is determined by the "timeshift" argument. Each buffer is emitted after a fixed timespan * specified by the "timespan" argument. When the source Observable completes or encounters an error, the * current buffer is emitted and the event is propagated. - * + * * @param timespan * The period of time each buffer is collecting values before it should be emitted. * @param timeshift @@ -3161,212 +1261,93 @@ public Observable> buffer(long timespan, long timeshift, TimeUnit unit) * @param scheduler * The {@link Scheduler} to use when determining the end and start of a buffer. * @return - * An {@link Observable} which produces new buffers periodically, and these are emitted after - * a fixed timespan has elapsed. + * An {@link Observable} which produces new buffers periodically, and these are emitted after + * a fixed timespan has elapsed. */ public Observable> buffer(long timespan, long timeshift, TimeUnit unit, Scheduler scheduler) { - return buffer(this, timespan, timeshift, unit, scheduler); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * 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 - * all of the Observalbes; 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 - * {@code onNext} as many times as the number of {@code onNext} invokations of the - * source Observable that emits the fewest items. - *

- * - * - * @param ws - * An Observable of source Observables - * @param reduceFunction - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable> ws, final FuncN reduceFunction) { - return ws.toList().mapMany(new Func1>, Observable>() { - @Override - public Observable call(List> wsList) { - return create(OperationZip.zip(wsList, reduceFunction)); - } - }); - } - - /** - * Returns an Observable that emits the results of a function of your choosing applied to - * combinations of four items emitted, in sequence, by four other Observables. - *

- * 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 - * all of the Observalbes; 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 Observable returned from zip will invoke - * onNext as many times as the number of onNext invocations of the - * source Observable that emits the fewest items. - *

- * - * - * @param ws - * An Observable of source Observables - * @param function - * a function that, when applied to an item emitted by each of the source - * Observables, results in an item that will be emitted by the resulting Observable - * @return an Observable that emits the zipped results - */ - public static Observable zip(Observable> ws, final Object function) { - @SuppressWarnings({ "unchecked" }) - final FuncN _f = Functions.from(function); - return zip(ws, _f); + return create(OperationBuffer.buffer(this, timespan, timeshift, unit, scheduler)); } /** * Returns an Observable that emits the results of a function of your choosing applied to * 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 + *

{@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 * all of the Observalbes; 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 - * {@code onNext} as many times as the number of {@code onNext} invokations of the + * The resulting {@code Observable} returned from {@code zip} will invoke {@code onNext} as many times as the number of {@code onNext} invokations of the * source Observable that emits the fewest items. *

* - * + * * @param ws - * A collection of source Observables + * An Observable of source Observables * @param reduceFunction * a function that, when applied to an item emitted by each of the source * Observables, results in an item that will be emitted by the resulting Observable * @return an Observable that emits the zipped results */ - public static Observable zip(Collection> ws, FuncN reduceFunction) { - return create(OperationZip.zip(ws, reduceFunction)); + public static Observable zip(Observable> ws, final FuncN reduceFunction) { + return ws.toList().mapMany(new Func1>, Observable>() { + @Override + public Observable call(List> wsList) { + return create(OperationZip.zip(wsList, reduceFunction)); + } + }); } /** * Returns an Observable that emits the results of a function of your choosing applied to * 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 + *

{@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 * all of the Observalbes; 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 - * {@code onNext} as many times as the number of {@code onNext} invocations of the + * The resulting {@code Observable} returned from {@code zip} will invoke {@code onNext} as many times as the number of {@code onNext} invokations of the * source Observable that emits the fewest items. *

* - * + * * @param ws * A collection of source Observables - * @param function + * @param reduceFunction * a function that, when applied to an item emitted by each of the source * Observables, results in an item that will be emitted by the resulting Observable * @return an Observable that emits the zipped results */ - public static Observable zip(Collection> ws, final Object function) { - @SuppressWarnings({ "unchecked" }) - final FuncN _f = Functions.from(function); - return zip(ws, _f); - } - - /** - * Combines the given observables, emitting an event containing an aggregation of the latest values of each of the source observables - * each time an event is received from one of the source observables, where the aggregation is defined by the given function. - *

- * - * - * @param w0 - * The first source observable. - * @param w1 - * The second source observable. - * @param combineFunction - * The aggregation function used to combine the source observable values. - * @return An Observable that combines the source Observables with the given combine function - */ - public static Observable combineLatest(Observable w0, Observable w1, Func2 combineFunction) { - return create(OperationCombineLatest.combineLatest(w0, w1, combineFunction)); - } - - /** - * @see #combineLatest(Observable, Observable, Func2) - */ - public static Observable combineLatest(Observable w0, Observable w1, Observable w2, Func3 combineFunction) { - return create(OperationCombineLatest.combineLatest(w0, w1, w2, combineFunction)); - } - - /** - * @see #combineLatest(Observable, Observable, Func2) - */ - public static Observable combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Func4 combineFunction) { - return create(OperationCombineLatest.combineLatest(w0, w1, w2, w3, combineFunction)); + public static Observable zip(Collection> ws, FuncN reduceFunction) { + return create(OperationZip.zip(ws, reduceFunction)); } /** - * Filters an Observable by discarding any of its items that do not satisfy the given 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 in the original Observable that the filter * evaluates as {@code true} */ public Observable filter(Func1 predicate) { - return filter(this, predicate); + return create(OperationFilter.filter(this, predicate)); } /** - * Registers an {@link Action0} to be called when this Observable invokes - * {@link Observer#onCompleted onCompleted} or {@link Observer#onError onError}. + * Registers an {@link Action0} to be called when this Observable invokes {@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 MSDN: Observable.Finally Method */ public Observable finallyDo(Action0 action) { return create(OperationFinally.finallyDo(this, action)); } - /** - * Filters an Observable by discarding any of its items that do not satisfy the given predicate. - *

- * - * - * @param callback - * 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 in the original Observable that the filter - * evaluates as {@code true} - */ - public Observable filter(final Object callback) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(callback); - return filter(this, new Func1() { - - @Override - public Boolean call(T t1) { - return (Boolean) _f.call(t1); - } - }); - } - /** * Creates a new Observable by applying a function that you supply to each item emitted by * the source Observable, where that function returns an Observable, and then merging those @@ -3375,7 +1356,7 @@ public Boolean call(T t1) { * *

* Note: {@code mapMany} and {@code flatMap} are equivalent. - * + * * @param func * a function that, when applied to an item emitted by the source Observable, returns * an Observable @@ -3389,40 +1370,17 @@ public Observable flatMap(Func1> func) { } /** - * Creates a new Observable by applying a function that you supply to each item emitted by - * the source Observable, where that function returns an Observable, and then merging those - * resulting Observables and emitting the results of this merger. - *

- * - *

- * Note: mapMany and flatMap are equivalent. - * - * @param callback - * a function that, when applied to an item emitted by the source Observable, returns - * an Observable - * @return an Observable that emits the result of applying the transformation function to each - * item emitted by the source Observable and merging the results of the Observables - * obtained from this transformation. - * @see #mapMany(Object) - */ - public Observable flatMap(final Object callback) { - return mapMany(callback); - } - - /** - * Filters an Observable by discarding any items it emits that do not satisfy the given predicate *

* - * + * * @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 in the original Observable that the filter * evaluates as {@code true} * @see #filter(Func1) */ public Observable where(Func1 predicate) { - return where(this, predicate); + return filter(predicate); } /** @@ -3430,38 +1388,14 @@ public Observable where(Func1 predicate) { * Observable and emits the result. *

* - * + * * @param func * a function to apply to each item emitted by the Observable * @return an Observable that emits the items from the source Observable, transformed by the * given function */ public Observable map(Func1 func) { - return map(this, func); - } - - /** - * Returns an Observable that applies the given function to each item emitted by an - * Observable and emits the result. - *

- * - * - * @param callback - * a function to apply to each item emitted by the Observable - * @return an Observable that emits the items from the source Observable, transformed by the - * given function - */ - public Observable map(final Object callback) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(callback); - return map(this, new Func1() { - - @Override - @SuppressWarnings("unchecked") - public R call(T t1) { - return (R) _f.call(t1); - } - }); + return create(OperationMap.map(this, func)); } /** @@ -3472,7 +1406,7 @@ public R call(T t1) { * *

* Note: mapMany and flatMap are equivalent. - * + * * @param func * a function that, when applied to an item emitted by the source Observable, returns * an Observable @@ -3482,80 +1416,48 @@ public R call(T t1) { * @see #flatMap(Func1) */ public Observable mapMany(Func1> func) { - return mapMany(this, func); - } - - /** - * Creates a new Observable by applying a function that you supply to each item emitted by - * the source Observable, where that function returns an Observable, and then merging those - * resulting Observables and emitting the results of this merger. - *

- * - *

- * Note: mapMany and flatMap are equivalent. - * - * @param callback - * a function that, when applied to an item emitted by the source Observable, returns - * an Observable - * @return an Observable that emits the result of applying the transformation function to each - * item emitted by the source Observable and merging the results of the Observables - * obtained from this transformation. - * @see #flatMap(Object) - */ - public Observable mapMany(final Object callback) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(callback); - return mapMany(this, new Func1>() { - - @Override - @SuppressWarnings("unchecked") - public Observable call(T t1) { - return (Observable) _f.call(t1); - } - }); + return create(OperationMap.mapMany(this, func)); } /** - * Turns all of the notifications from a source Observable into {@link Observer#onNext onNext} - * emissions, and marks them with their original notification types within {@link Notification} - * objects. + * Turns all of the notifications from a source Observable into {@link Observer#onNext onNext} emissions, and marks them with their original notification types within {@link Notification} objects. *

* - * + * * @return an Observable whose items are the result of materializing the items and * notifications of the source Observable * @see MSDN: Observable.materialize */ public Observable> materialize() { - return materialize(this); + return create(OperationMaterialize.materialize(this)); } /** * Asynchronously subscribes and unsubscribes Observers on the specified {@link Scheduler}. *

* - * + * * @param scheduler * the {@link Scheduler} to perform subscription and unsubscription actions on * @return the source Observable modified so that its subscriptions and unsubscriptions happen * on the specified {@link Scheduler} */ public Observable subscribeOn(Scheduler scheduler) { - return subscribeOn(this, scheduler); + return create(OperationSubscribeOn.subscribeOn(this, scheduler)); } /** * Asynchronously notify {@link Observer}s on the specified {@link Scheduler}. *

* - * + * * @param scheduler * the {@link Scheduler} to notify {@link Observer}s on * @return the source Observable modified so that its {@link Observer}s are notified on the * specified {@link Scheduler} */ public Observable observeOn(Scheduler scheduler) { - return observeOn(this, scheduler); + return create(OperationObserveOn.observeOn(this, scheduler)); } /** @@ -3564,21 +1466,19 @@ public Observable observeOn(Scheduler scheduler) { * or notifications they represent. *

* - * - * @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 * @see MSDN: Observable.dematerialize * @throws Throwable * if the source Observable is not of type {@code Observable>}. */ @SuppressWarnings("unchecked") public Observable dematerialize() { - return dematerialize((Observable>) this); + return create(OperationDematerialize.dematerialize((Observable>) this)); } /** - * 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. *

* *

@@ -3589,62 +1489,24 @@ public Observable dematerialize() { * function that returns an Observable (resumeFunction) to * onErrorResumeNext, if the original Observable encounters an error, instead of * invoking its Observer's onError method, it will instead relinquish control to - * the Observable returned from resumeFunction, which will invoke the Observer's - * {@link Observer#onNext onNext} method if it is able to do so. In such a case, because no + * the Observable returned from resumeFunction, 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 onError, the Observer may never know that an * error happened. *

* You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. - * + * * @param resumeFunction * a function that returns an Observable that will take over if the source Observable * encounters an error * @return the original Observable, with appropriately modified behavior */ public Observable onErrorResumeNext(final Func1> resumeFunction) { - return onErrorResumeNext(this, resumeFunction); - } - - /** - * Instruct an Observable to emit an item (returned by a specified function) 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 - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorReturn method changes this behavior. If you pass a function - * (resumeFunction) to an Observable's onErrorReturn method, if the - * original Observable encounters an error, instead of invoking its Observer's - * onError function, it will instead pass the return value of - * resumeFunction to the Observer's {@link Observer#onNext onNext} method. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param resumeFunction - * a function that returns an item that the Observable will emit if the source - * Observable encounters an error - * @return the original Observable with appropriately modified behavior - */ - public Observable onErrorResumeNext(final Object resumeFunction) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(resumeFunction); - return onErrorResumeNext(this, new Func1>() { - - @Override - @SuppressWarnings("unchecked") - public Observable call(Throwable e) { - return (Observable) _f.call(e); - } - }); + return create(OperationOnErrorResumeNextViaFunction.onErrorResumeNextViaFunction(this, resumeFunction)); } /** - * 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. *

* *

@@ -3655,26 +1517,24 @@ public Observable call(Throwable e) { * another Observable (resumeSequence) to an Observable's * onErrorResumeNext method, if the original Observable encounters an error, * instead of invoking its Observer's onError method, it will instead relinquish - * control to 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 + * control to 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 onError, the Observer may never know that an * error happened. *

* You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. - * + * * @param resumeSequence * a function that returns an Observable that will take over if the source Observable * encounters an error * @return the original Observable, with appropriately modified behavior */ public Observable onErrorResumeNext(final Observable resumeSequence) { - return onErrorResumeNext(this, resumeSequence); + return create(OperationOnErrorResumeNextViaObservable.onErrorResumeNextViaObservable(this, resumeSequence)); } - + /** - * Instruct an Observable to pass control to another Observable rather than invoking - * {@link Observer#onError onError} if it encounters an error of type {@link java.lang.Exception}. + * Instruct an Observable to pass control to another Observable rather than invoking {@link Observer#onError onError} if it encounters an error of type {@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. *

@@ -3687,21 +1547,20 @@ public Observable onErrorResumeNext(final Observable resumeSequence) { * another Observable (resumeSequence) to an Observable's * onErrorResumeNext method, if the original Observable encounters an error, * instead of invoking its Observer's onError method, it will instead relinquish - * control to 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 + * control to 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 onError, the Observer may never know that an * error happened. *

* You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. - * + * * @param resumeSequence * a function that returns an Observable that will take over if the source Observable * encounters an error * @return the original Observable, with appropriately modified behavior */ public Observable onExceptionResumeNext(final Observable resumeSequence) { - return onExceptionResumeNext(this, resumeSequence); + return create(OperationOnExceptionResumeNextViaObservable.onExceptionResumeNextViaObservable(this, resumeSequence)); } /** @@ -3721,50 +1580,14 @@ public Observable onExceptionResumeNext(final Observable resumeSequence) { *

* You can use this to prevent errors from propagating or to supply fallback data should errors * be encountered. - * + * * @param resumeFunction * a function that returns an item that the new Observable will emit if the source * Observable encounters an error * @return the original Observable with appropriately modified behavior */ public Observable onErrorReturn(Func1 resumeFunction) { - return onErrorReturn(this, resumeFunction); - } - - /** - * Instruct an Observable to emit a particular item 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 - * onError method, and then quits without invoking any more of its Observer's - * methods. The onErrorReturn method changes this behavior. If you pass a function - * (resumeFunction) to an Observable's onErrorReturn method, if the - * original Observable encounters an error, instead of invoking its Observer's - * onError function, it will instead pass the return value of - * resumeFunction to the Observer's {@link Observer#onNext onNext} method. - *

- * You can use this to prevent errors from propagating or to supply fallback data should errors - * be encountered. - * - * @param resumeFunction - * a function that returns an item that the new Observable will emit if the source - * Observable encounters an error - * @return the original Observable with appropriately modified behavior - */ - public Observable onErrorReturn(final Object resumeFunction) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(resumeFunction); - return onErrorReturn(this, new Func1() { - - @Override - @SuppressWarnings("unchecked") - public T call(Throwable e) { - return (T) _f.call(e); - } - }); + return create(OperationOnErrorReturn.onErrorReturn(this, resumeFunction)); } /** @@ -3779,7 +1602,7 @@ public T call(Throwable e) { * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, * has an inject method that does a similar operation on lists. - * + * * @param accumulator * An accumulator function to be invoked on each item emitted by the source * Observable, whose result will be used in the next accumulator call @@ -3789,21 +1612,20 @@ public T call(Throwable e) { * @see Wikipedia: Fold (higher-order function) */ public Observable reduce(Func2 accumulator) { - return reduce(this, accumulator); + return create(OperationScan.scan(this, accumulator)).takeLast(1); } /** * 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}. *

* - * + * * @return a {@link ConnectableObservable} that upon connection causes the source Observable to * emit items to its {@link Observer}s */ public ConnectableObservable replay() { - return replay(this); + return OperationMulticast.multicast(this, ReplaySubject. create()); } /** @@ -3818,59 +1640,36 @@ public ConnectableObservable replay() { * NOTE: You sacrifice the ability to unsubscribe from the origin when you use the * 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 notifications for * the benefit of subsequent subscribers. */ public Observable cache() { - return cache(this); + return create(OperationCache.cache(this)); } /** - * 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. *

* - * + * * @return a {@link ConnectableObservable} that upon connection causes the source Observable to * emit items to its {@link Observer}s */ public ConnectableObservable publish() { - return publish(this); - } - - /** - * A version of reduce() for use by dynamic languages. - *

- * - * - * @see #reduce(Func2) - */ - public Observable reduce(Object accumulator) { - return reduce(this, accumulator); + return OperationMulticast.multicast(this, PublishSubject. create()); } /** * Synonymous with reduce(). *

* - * + * * @see #reduce(Func2) */ public Observable aggregate(Func2 accumulator) { - return aggregate(this, accumulator); - } - - /** - * A version of aggregate() for use by dynamic languages. - *

- * - * - * @see #reduce(Func2) - */ - public Observable aggregate(Object accumulator) { - return aggregate(this, accumulator); + return reduce(accumulator); } /** @@ -3885,7 +1684,7 @@ public Observable aggregate(Object accumulator) { * This technique, which is called "reduce" or "aggregate" here, is sometimes called "fold," * "accumulate," "compress," or "inject" in other programming contexts. Groovy, for instance, * has an inject method that does a similar operation on lists. - * + * * @param initialValue * the initial (seed) accumulator value * @param accumulator @@ -3897,40 +1696,18 @@ public Observable aggregate(Object accumulator) { * @see Wikipedia: Fold (higher-order function) */ public Observable reduce(R initialValue, Func2 accumulator) { - return reduce(this, initialValue, accumulator); - } - - /** - * A version of reduce() for use by dynamic languages. - *

- * - * - * @see #reduce(Object, Func2) - */ - public Observable reduce(R initialValue, Object accumulator) { - return reduce(this, initialValue, accumulator); + return create(OperationScan.scan(this, initialValue, accumulator)).takeLast(1); } /** * Synonymous with reduce(). *

* - * + * * @see #reduce(Object, Func2) */ public Observable aggregate(R initialValue, Func2 accumulator) { - return aggregate(this, initialValue, accumulator); - } - - /** - * A version of aggregate() for use by dynamic languages. - *

- * - * - * @see #reduce(Object, Func2) - */ - public Observable aggregate(R initialValue, Object accumulator) { - return aggregate(this, initialValue, accumulator); + return reduce(initialValue, accumulator); } /** @@ -3945,16 +1722,15 @@ public Observable aggregate(R initialValue, Object accumulator) { *

* Note that when you pass a seed to scan() the resulting Observable will emit * that seed as its first emitted 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 MSDN: Observable.Scan */ public Observable scan(Func2 accumulator) { - return scan(this, accumulator); + return create(OperationScan.scan(this, accumulator)); } /** @@ -3962,7 +1738,7 @@ public Observable scan(Func2 accumulator) { * Observable at a specified time interval. *

* - * + * * @param period * the sampling rate * @param unit @@ -3979,7 +1755,7 @@ public Observable sample(long period, TimeUnit unit) { * Observable at a specified time interval. *

* - * + * * @param period * the sampling rate * @param unit @@ -3993,17 +1769,6 @@ public Observable sample(long period, TimeUnit unit, Scheduler scheduler) { return create(OperationSample.sample(this, period, unit, scheduler)); } - /** - * A version of scan() for use by dynamic languages. - *

- * - * - * @see #scan(Func2) - */ - public Observable scan(final Object accumulator) { - return scan(this, accumulator); - } - /** * Returns an Observable that applies a function of your choosing to the first item emitted by a * source Observable, then feeds the result of that function along with the second item emitted @@ -4016,30 +1781,17 @@ public Observable scan(final Object accumulator) { *

* Note that when you pass a seed to scan() the resulting Observable will emit * that seed as its first emitted item. - * + * * @param initialValue * the initial (seed) accumulator value * @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 MSDN: - * Observable.Scan + * @see MSDN: Observable.Scan */ public Observable scan(R initialValue, Func2 accumulator) { - return scan(this, initialValue, accumulator); - } - - /** - * A version of scan() for use by dynamic languages. - *

- * - * - * @see #scan(Object, Func2) - */ - public Observable scan(final R initialValue, final Object accumulator) { - return scan(this, initialValue, accumulator); + return create(OperationScan.scan(this, initialValue, accumulator)); } /** @@ -4047,29 +1799,14 @@ public Observable scan(final R initialValue, final Object accumulator) { * the source Observable satisfy a condition. *

* - * + * * @param predicate * a function that evaluates an item and returns a Boolean * @return an Observable that emits true if all items emitted by the source * Observable satisfy the predicate; otherwise, false */ public Observable all(Func1 predicate) { - return all(this, predicate); - } - - /** - * Returns an Observable that emits a Boolean that indicates whether all of the items emitted by - * the source Observable satisfy a condition. - *

- * - * - * @param predicate - * a function that evaluates an item and returns a Boolean - * @return an Observable that emits true if all items emitted by the source - * Observable satisfy the predicate; otherwise, false - */ - public Observable all(Object predicate) { - return all(this, predicate); + return create(OperationAll.all(this, predicate)); } /** @@ -4080,14 +1817,14 @@ public Observable all(Object predicate) { *

* You can ignore the first num items emitted by an Observable and attend only to * those items that come after, by modifying the Observable with the skip method. - * + * * @param num * the number of items to skip * @return an Observable that is identical to the source Observable except that it does not * emit the first num items that the source emits */ public Observable skip(int num) { - return skip(this, num); + return create(OperationSkip.skip(this, num)); } /** @@ -4096,34 +1833,17 @@ public Observable skip(int num) { *

* *

- * This method returns an Observable that will invoke a subscribing {@link Observer}'s - * {@link Observer#onNext onNext} function a maximum of 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 num times before invoking * {@link Observer#onCompleted onCompleted}. - * + * * @param num * the number of items to take * @return an Observable that emits only the first num items from the source - * Observable, or all of the items from the source Observable if that Observable emits - * fewer than num items - */ - public Observable take(final int num) { - return take(this, num); - } - - /** - * Returns an Observable that emits items emitted by the source Observable so long as a - * specified condition is true. - *

- * - * - * @param predicate - * a function that evaluates an item emitted by the source Observable and returns a - * Boolean - * @return an Observable that emits the items from the source Observable so long as each item - * satisfies the condition defined by predicate + * Observable, or all of the items from the source Observable if that Observable emits + * fewer than num items */ - public Observable takeWhile(final Func1 predicate) { - return takeWhile(this, predicate); + public Observable take(final int num) { + return create(OperationTake.take(this, num)); } /** @@ -4131,15 +1851,15 @@ public Observable takeWhile(final Func1 predicate) { * specified condition is true. *

* - * + * * @param predicate * a function that evaluates an item emitted by the source Observable and returns a * Boolean * @return an Observable that emits the items from the source Observable so long as each item * satisfies the condition defined by predicate */ - public Observable takeWhile(final Object predicate) { - return takeWhile(this, predicate); + public Observable takeWhile(final Func1 predicate) { + return create(OperationTakeWhile.takeWhile(this, predicate)); } /** @@ -4148,7 +1868,7 @@ public Observable takeWhile(final Object predicate) { * relative to the complete sequence. *

* - * + * * @param predicate * a function to test each item emitted by the source Observable for a condition; * the second parameter of the function represents the index of the source item @@ -4156,25 +1876,7 @@ public Observable takeWhile(final Object predicate) { * continues to return true for each item, then completes */ public Observable takeWhileWithIndex(final Func2 predicate) { - return takeWhileWithIndex(this, predicate); - } - - /** - * Returns an Observable that emits the items emitted by a source Observable so long as a given - * predicate remains true, where the predicate can operate on both the item and its index - * relative to the complete sequence. - *

- * - * - * @param predicate - * a function that evaluates an item emitted by the source Observable and returns a - * Boolean; the second parameter of the function represents the index of the source - * item - * @return an Observable that emits items from the source Observable so long as the predicate - * continues to return true for each item, then completes - */ - public Observable takeWhileWithIndex(final Object predicate) { - return takeWhileWithIndex(this, predicate); + return create(OperationTakeWhile.takeWhileWithIndex(this, predicate)); } /** @@ -4182,7 +1884,7 @@ public Observable takeWhileWithIndex(final Object predicate) { * Observable. *

* - * + * * @param count * the number of items to emit from the end of the sequence emitted by the source * Observable @@ -4190,7 +1892,7 @@ public Observable takeWhileWithIndex(final Object predicate) { * Observable */ public Observable takeLast(final int count) { - return takeLast(this, count); + return create(OperationTakeLast.takeLast(this, count)); } /** @@ -4198,7 +1900,7 @@ public Observable takeLast(final int count) { * other Observable emits an item. *

* - * + * * @param other * the Observable whose first emitted item will cause takeUntil to stop * emitting items from the source Observable @@ -4208,7 +1910,7 @@ public Observable takeLast(final int count) { * other emits its first item */ public Observable takeUntil(Observable other) { - return takeUntil(this, other); + return OperationTakeUntil.takeUntil(this, other); } /** @@ -4217,21 +1919,19 @@ public Observable takeUntil(Observable other) { *

* *

- * 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 onNext function once, passing it the entire list, by - * calling the Observable's toList method prior to calling its {@link #subscribe} - * method. + * calling the Observable's toList method prior to calling its {@link #subscribe} method. *

* 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. - * + * * @return an Observable that emits a single item: a List containing all of the items emitted by * the source Observable. */ public Observable> toList() { - return toList(this); + return create(OperationToObservableList.toObservableList(this)); } /** @@ -4240,14 +1940,14 @@ public Observable> toList() { * all other items in the sequence). *

* - * + * * @throws ClassCastException * if any item emitted by the Observable does not implement {@link Comparable} with * respect to all other items emitted by the Observable * @return an Observable that emits the items from the source Observable in sorted order */ public Observable> toSortedList() { - return toSortedList(this); + return create(OperationToObservableSortedList.toSortedList(this)); } /** @@ -4255,36 +1955,21 @@ public Observable> toSortedList() { * order based on a specified comparison function *

* - * + * * @param sortFunction * a function that compares two items emitted by the source Observable and returns * an Integer that indicates their sort order * @return an Observable that emits the items from the source Observable in sorted order */ public Observable> toSortedList(Func2 sortFunction) { - return toSortedList(this, sortFunction); - } - - /** - * Return an Observable that emits the items emitted by the source Observable, in a sorted - * order based on a specified comparison function - *

- * - * - * @param sortFunction - * a function that compares two items emitted by the source Observable and returns - * an Integer that indicates their sort order - * @return an Observable that emits the items from the source Observable in sorted order - */ - public Observable> toSortedList(final Object sortFunction) { - return toSortedList(this, sortFunction); + return create(OperationToObservableSortedList.toSortedList(this, sortFunction)); } /** * Emit a specified set of items before beginning to emit items from the source Observable. *

* - * + * * @param values * the items you want the modified Observable to emit first * @return an Observable that exhibits the modified behavior @@ -4299,7 +1984,7 @@ public Observable startWith(T... values) { * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. *

* - * + * * @param keySelector * a function that extracts the key from an item * @param elementSelector @@ -4313,29 +1998,7 @@ public Observable startWith(T... values) { * share that key value */ public Observable> groupBy(final Func1 keySelector, final Func1 elementSelector) { - return groupBy(this, keySelector, elementSelector); - } - - /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. - *

- * - * - * @param keySelector - * a function that extracts the key from an item - * @param elementSelector - * a function to map a source item to an item in a {@link GroupedObservable} - * @param - * the key type - * @param - * the type of items emitted by the resulting {@link GroupedObservable}s - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value - */ - public Observable> groupBy(final Object keySelector, final Object elementSelector) { - return groupBy(this, keySelector, elementSelector); + return create(OperationGroupBy.groupBy(this, keySelector, elementSelector)); } /** @@ -4343,7 +2006,7 @@ public Observable> groupBy(final Object keySelect * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. *

* - * + * * @param keySelector * a function that extracts the key for each item * @param @@ -4353,33 +2016,14 @@ public Observable> groupBy(final Object keySelect * share that key value */ public Observable> groupBy(final Func1 keySelector) { - return groupBy(this, keySelector); - } - - /** - * Groups the items emitted by an Observable according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s, one GroupedObservable per group. - *

- * - * - * @param keySelector - * a function that extracts the key for each item - * @param - * the key type - * @return an Observable that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and emits items representing items from the source Observable that - * share that key value - */ - public Observable> groupBy(final Object keySelector) { - return groupBy(this, keySelector); + return create(OperationGroupBy.groupBy(this, keySelector)); } /** * Converts an Observable into a {@link BlockingObservable} (an Observable with blocking * operators). - * - * @see Blocking - * Observable Operators + * + * @see Blocking Observable Operators */ public BlockingObservable toBlockingObservable() { return BlockingObservable.from(this); @@ -4389,9 +2033,9 @@ public BlockingObservable toBlockingObservable() { * Whether a given {@link Function} is an internal implementation inside rx.* packages or not. *

* For why this is being used see https://github.com/Netflix/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls to user code from within an operator" - * + * * 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 f * @return {@code true} if the given function is an internal implementation, and {@code false} otherwise. */ @@ -4407,517 +2051,4 @@ private boolean isInternalImplementation(Object o) { return p != null && p.getName().startsWith("rx.operators"); } - public static class UnitTest { - - @Mock - Observer w; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testCreate() { - - Observable observable = create(new Func1, Subscription>() { - - @Override - public Subscription call(Observer Observer) { - Observer.onNext("one"); - Observer.onNext("two"); - Observer.onNext("three"); - Observer.onCompleted(); - return Subscriptions.empty(); - } - - }); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, times(1)).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, times(1)).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - - @Test - public void testReduce() { - Observable Observable = toObservable(1, 2, 3, 4); - reduce(Observable, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t1 + t2; - } - - }).subscribe(w); - // we should be called only once - verify(w, times(1)).onNext(anyInt()); - verify(w).onNext(10); - } - - @Test - public void testReduceWithInitialValue() { - Observable Observable = toObservable(1, 2, 3, 4); - reduce(Observable, 50, new Func2() { - - @Override - public Integer call(Integer t1, Integer t2) { - return t1 + t2; - } - - }).subscribe(w); - // we should be called only once - verify(w, times(1)).onNext(anyInt()); - verify(w).onNext(60); - } - - @Test - public void testSequenceEqual() { - Observable first = toObservable(1, 2, 3); - Observable second = toObservable(1, 2, 4); - @SuppressWarnings("unchecked") - Observer result = mock(Observer.class); - sequenceEqual(first, second).subscribe(result); - verify(result, times(2)).onNext(true); - verify(result, times(1)).onNext(false); - } - - @Test - public void testOnSubscribeFails() { - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - final RuntimeException re = new RuntimeException("bad impl"); - Observable o = Observable.create(new Func1, Subscription>() { - - @Override - public Subscription call(Observer t1) { - throw re; - } - - }); - o.subscribe(observer); - verify(observer, times(0)).onNext(anyString()); - verify(observer, times(0)).onCompleted(); - verify(observer, times(1)).onError(re); - } - - @Test - public void testMaterializeDematerializeChaining() { - Observable obs = Observable.just(1); - Observable chained = obs.materialize().dematerialize(); - - @SuppressWarnings("unchecked") - Observer observer = mock(Observer.class); - chained.subscribe(observer); - - verify(observer, times(1)).onNext(1); - verify(observer, times(1)).onCompleted(); - verify(observer, times(0)).onError(any(Throwable.class)); - } - - /** - * The error from the user provided Observer is not handled by the subscribe method try/catch. - * - * It is handled by the AtomicObserver that wraps the provided Observer. - * - * Result: Passes (if AtomicObserver functionality exists) - */ - @Test - public void testCustomObservableWithErrorInObserverAsynchronous() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); - Observable.create(new Func1, Subscription>() { - - @Override - public Subscription call(final Observer observer) { - final BooleanSubscription s = new BooleanSubscription(); - new Thread(new Runnable() { - - @Override - public void run() { - try { - if (!s.isUnsubscribed()) { - observer.onNext("1"); - observer.onNext("2"); - observer.onNext("three"); - observer.onNext("4"); - observer.onCompleted(); - } - } finally { - latch.countDown(); - } - } - }).start(); - return s; - } - }).subscribe(new Observer() { - @Override - public void onCompleted() { - System.out.println("completed"); - } - - @Override - public void onError(Throwable e) { - error.set(e); - System.out.println("error"); - e.printStackTrace(); - } - - @Override - public void onNext(String v) { - int num = Integer.parseInt(v); - System.out.println(num); - // doSomething(num); - count.incrementAndGet(); - } - - }); - - // wait for async sequence to complete - latch.await(); - - assertEquals(2, count.get()); - assertNotNull(error.get()); - if (!(error.get() instanceof NumberFormatException)) { - fail("It should be a NumberFormatException"); - } - } - - /** - * The error from the user provided Observer is handled by the subscribe try/catch because this is synchronous - * - * Result: Passes - */ - @Test - public void testCustomObservableWithErrorInObserverSynchronous() { - final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); - Observable.create(new Func1, Subscription>() { - - @Override - public Subscription call(Observer observer) { - observer.onNext("1"); - observer.onNext("2"); - observer.onNext("three"); - observer.onNext("4"); - observer.onCompleted(); - return Subscriptions.empty(); - } - }).subscribe(new Observer() { - - @Override - public void onCompleted() { - System.out.println("completed"); - } - - @Override - public void onError(Throwable e) { - error.set(e); - System.out.println("error"); - e.printStackTrace(); - } - - @Override - public void onNext(String v) { - int num = Integer.parseInt(v); - System.out.println(num); - // doSomething(num); - count.incrementAndGet(); - } - - }); - assertEquals(2, count.get()); - assertNotNull(error.get()); - if (!(error.get() instanceof NumberFormatException)) { - fail("It should be a NumberFormatException"); - } - } - - /** - * The error from the user provided Observable is handled by the subscribe try/catch because this is synchronous - * - * - * Result: Passes - */ - @Test - public void testCustomObservableWithErrorInObservableSynchronous() { - final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); - Observable.create(new Func1, Subscription>() { - - @Override - public Subscription call(Observer observer) { - observer.onNext("1"); - observer.onNext("2"); - throw new NumberFormatException(); - } - }).subscribe(new Observer() { - - @Override - public void onCompleted() { - System.out.println("completed"); - } - - @Override - public void onError(Throwable e) { - error.set(e); - System.out.println("error"); - e.printStackTrace(); - } - - @Override - public void onNext(String v) { - System.out.println(v); - count.incrementAndGet(); - } - - }); - assertEquals(2, count.get()); - assertNotNull(error.get()); - if (!(error.get() instanceof NumberFormatException)) { - fail("It should be a NumberFormatException"); - } - } - - @Test - public void testPublish() throws InterruptedException { - final AtomicInteger counter = new AtomicInteger(); - ConnectableObservable o = Observable.create(new Func1, Subscription>() { - - @Override - public Subscription call(final Observer observer) { - final BooleanSubscription subscription = new BooleanSubscription(); - new Thread(new Runnable() { - - @Override - public void run() { - counter.incrementAndGet(); - observer.onNext("one"); - observer.onCompleted(); - } - }).start(); - return subscription; - } - }).publish(); - - final CountDownLatch latch = new CountDownLatch(2); - - // subscribe once - o.subscribe(new Action1() { - - @Override - public void call(String v) { - assertEquals("one", v); - latch.countDown(); - } - }); - - // subscribe again - o.subscribe(new Action1() { - - @Override - public void call(String v) { - assertEquals("one", v); - latch.countDown(); - } - }); - - Subscription s = o.connect(); - try { - if (!latch.await(1000, TimeUnit.MILLISECONDS)) { - fail("subscriptions did not receive values"); - } - assertEquals(1, counter.get()); - } finally { - s.unsubscribe(); - } - } - - @Test - public void testReplay() throws InterruptedException { - final AtomicInteger counter = new AtomicInteger(); - ConnectableObservable o = Observable.create(new Func1, Subscription>() { - - @Override - public Subscription call(final Observer observer) { - final BooleanSubscription subscription = new BooleanSubscription(); - new Thread(new Runnable() { - - @Override - public void run() { - counter.incrementAndGet(); - observer.onNext("one"); - observer.onCompleted(); - } - }).start(); - return subscription; - } - }).replay(); - - // we connect immediately and it will emit the value - Subscription s = o.connect(); - try { - - // we then expect the following 2 subscriptions to get that same value - final CountDownLatch latch = new CountDownLatch(2); - - // subscribe once - o.subscribe(new Action1() { - - @Override - public void call(String v) { - assertEquals("one", v); - latch.countDown(); - } - }); - - // subscribe again - o.subscribe(new Action1() { - - @Override - public void call(String v) { - assertEquals("one", v); - latch.countDown(); - } - }); - - if (!latch.await(1000, TimeUnit.MILLISECONDS)) { - fail("subscriptions did not receive values"); - } - assertEquals(1, counter.get()); - } finally { - s.unsubscribe(); - } - } - - @Test - public void testCache() throws InterruptedException { - final AtomicInteger counter = new AtomicInteger(); - Observable o = Observable.create(new Func1, Subscription>() { - - @Override - public Subscription call(final Observer observer) { - final BooleanSubscription subscription = new BooleanSubscription(); - new Thread(new Runnable() { - - @Override - public void run() { - counter.incrementAndGet(); - observer.onNext("one"); - observer.onCompleted(); - } - }).start(); - return subscription; - } - }).cache(); - - // we then expect the following 2 subscriptions to get that same value - final CountDownLatch latch = new CountDownLatch(2); - - // subscribe once - o.subscribe(new Action1() { - - @Override - public void call(String v) { - assertEquals("one", v); - latch.countDown(); - } - }); - - // subscribe again - o.subscribe(new Action1() { - - @Override - public void call(String v) { - assertEquals("one", v); - latch.countDown(); - } - }); - - if (!latch.await(1000, TimeUnit.MILLISECONDS)) { - fail("subscriptions did not receive values"); - } - assertEquals(1, counter.get()); - } - - /** - * https://github.com/Netflix/RxJava/issues/198 - * - * Rx Design Guidelines 5.2 - * - * "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be - * to rethrow the exception on the thread that the message comes out from the Observable. - * The OnCompleted behavior in this case is to do nothing." - */ - @Test - public void testErrorThrownWithoutErrorHandlerSynchronous() { - try { - error(new RuntimeException("failure")).subscribe(new Action1() { - - @Override - public void call(Object t1) { - // won't get anything - } - - }); - fail("expected exception"); - } catch (Throwable e) { - assertEquals("failure", e.getMessage()); - } - } - - /** - * https://github.com/Netflix/RxJava/issues/198 - * - * Rx Design Guidelines 5.2 - * - * "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be - * to rethrow the exception on the thread that the message comes out from the Observable. - * The OnCompleted behavior in this case is to do nothing." - * - * @throws InterruptedException - */ - @Test - public void testErrorThrownWithoutErrorHandlerAsynchronous() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference exception = new AtomicReference(); - Observable.create(new Func1, Subscription>() { - - @Override - public Subscription call(final Observer observer) { - new Thread(new Runnable() { - - @Override - public void run() { - try { - observer.onError(new Error("failure")); - } catch (Throwable e) { - // without an onError handler it has to just throw on whatever thread invokes it - exception.set(e); - } - latch.countDown(); - } - }).start(); - return Subscriptions.empty(); - } - }).subscribe(new Action1() { - - @Override - public void call(Object t1) { - - } - - }); - // wait for exception - latch.await(3000, TimeUnit.MILLISECONDS); - assertNotNull(exception.get()); - assertEquals("failure", exception.get().getMessage()); - } - } - } diff --git a/rxjava-core/src/main/java/rx/Scheduler.java b/rxjava-core/src/main/java/rx/Scheduler.java index 22735209cd..bcfd48bf68 100644 --- a/rxjava-core/src/main/java/rx/Scheduler.java +++ b/rxjava-core/src/main/java/rx/Scheduler.java @@ -151,39 +151,6 @@ public Subscription schedule(T state, Func2 acti } } - /** - * Schedules a cancelable action to be executed. - * - * @param action - * Action to schedule. - * @return a subscription to be able to unsubscribe from action. - */ - public Subscription schedule(final Func1 action) { - return schedule(null, new Func2() { - - @Override - public Subscription call(Scheduler scheduler, @SuppressWarnings("unused") Void state) { - return action.call(scheduler); - } - }); - } - - /** - * Schedules a cancelable action to be executed. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - public Subscription schedule(final Func0 action) { - return schedule(null, new Func2() { - - @Override - public Subscription call(@SuppressWarnings("unused") Scheduler scheduler, @SuppressWarnings("unused") Void state) { - return action.call(); - } - }); - } /** * Schedules an action to be executed. @@ -203,27 +170,6 @@ public Subscription call(@SuppressWarnings("unused") Scheduler scheduler, @Suppr }); } - /** - * Schedules a cancelable action to be executed in delayTime. - * - * @param action - * Action to schedule. - * @param delayTime - * Time the action is to be delayed before executing. - * @param unit - * Time unit of the delay time. - * @return a subscription to be able to unsubscribe from action. - */ - public Subscription schedule(final Func1 action, long delayTime, TimeUnit unit) { - return schedule(null, new Func2() { - - @Override - public Subscription call(Scheduler scheduler, @SuppressWarnings("unused") Void state) { - return action.call(scheduler); - } - }, delayTime, unit); - } - /** * Schedules an action to be executed in delayTime. * @@ -242,66 +188,6 @@ public Subscription call(@SuppressWarnings("unused") Scheduler scheduler, @Suppr }, delayTime, unit); } - /** - * Schedules a cancelable action to be executed in delayTime. - * - * @param action - * action - * @return a subscription to be able to unsubscribe from action. - */ - public Subscription schedule(final Func0 action, long delayTime, TimeUnit unit) { - return schedule(null, new Func2() { - - @Override - public Subscription call(@SuppressWarnings("unused") Scheduler scheduler, @SuppressWarnings("unused") Void state) { - return action.call(); - } - }, delayTime, unit); - } - - /** - * Schedules a cancelable action to be executed periodically. - * - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @param unit - * The time unit the interval above is given in. - * @return A subscription to be able to unsubscribe from action. - */ - public Subscription schedulePeriodically(final Func1 action, long initialDelay, long period, TimeUnit unit) { - return schedulePeriodically(null, new Func2() { - @Override - public Subscription call(Scheduler scheduler, @SuppressWarnings("unused") Void state) { - return action.call(scheduler); - } - }, initialDelay, period, unit); - } - - /** - * Schedules a cancelable action to be executed periodically. - * - * @param action - * The action to execute periodically. - * @param initialDelay - * Time to wait before executing the action for the first time. - * @param period - * The time interval to wait each time in between executing the action. - * @param unit - * The time unit the interval above is given in. - * @return A subscription to be able to unsubscribe from action. - */ - public Subscription schedulePeriodically(final Func0 action, long initialDelay, long period, TimeUnit unit) { - return schedulePeriodically(null, new Func2() { - @Override - public Subscription call(@SuppressWarnings("unused") Scheduler scheduler, @SuppressWarnings("unused") Void state) { - return action.call(); - } - }, initialDelay, period, unit); - } /** * Schedules an action to be executed periodically. diff --git a/rxjava-core/src/main/java/rx/observables/BlockingObservable.java b/rxjava-core/src/main/java/rx/observables/BlockingObservable.java index 2d826a8fea..eb16260d2b 100644 --- a/rxjava-core/src/main/java/rx/observables/BlockingObservable.java +++ b/rxjava-core/src/main/java/rx/observables/BlockingObservable.java @@ -117,22 +117,6 @@ public static T last(final Observable source, final Func1 pre return last(source.filter(predicate)); } - /** - * Returns the last item emitted by an {@link Observable} that matches a given predicate. - *

- * - * - * @param source - * the source {@link Observable} - * @param predicate - * a predicate function to evaluate items emitted by the {@link Observable} - * @return the last item emitted by the {@link Observable} for which the predicate function - * returns true - */ - public static T last(final Observable source, final Object predicate) { - return last(source.filter(predicate)); - } - /** * Returns the last item emitted by an {@link Observable}, or a default value if no item is * emitted. @@ -173,35 +157,6 @@ public static T lastOrDefault(Observable source, T defaultValue, Func1 - * - * - * @param source - * the source {@link Observable} - * @param defaultValue - * a default value to return if the {@link Observable} emits no matching items - * @param predicate - * a predicate function to evaluate items emitted by the {@link Observable} - * @param - * the type of items emitted by the {@link Observable} - * @return the last item emitted by an {@link Observable} that matches the predicate, or the - * default value if no matching item is emitted - */ - public static T lastOrDefault(Observable source, T defaultValue, Object predicate) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(predicate); - - return lastOrDefault(source, defaultValue, new Func1() { - @Override - public Boolean call(T args) { - return (Boolean) _f.call(args); - } - }); - } - /** * Returns an {@link Iterable} that always returns the item most recently emitted by an * {@link Observable}. @@ -293,24 +248,6 @@ public static T single(Observable source, Func1 predicate) { return from(source).single(predicate); } - /** - * If the {@link Observable} completes after emitting a single item that matches a given - * predicate, return that item, otherwise throw an exception. - * - * - * @param source - * the source {@link Observable} - * @param predicate - * a predicate function to evaluate items emitted by the {@link Observable} - * @return the single item emitted by the source {@link Observable} that matches the predicate - * @throws IllegalStateException - * if the {@link Observable} does not emit exactly one item that matches the - * predicate - */ - public static T single(Observable source, Object predicate) { - return from(source).single(predicate); - } - /** * If the {@link Observable} completes after emitting a single item, return that item, otherwise * return a default value. @@ -347,25 +284,6 @@ public static T singleOrDefault(Observable source, T defaultValue, Func1< return from(source).singleOrDefault(defaultValue, predicate); } - /** - * If the {@link Observable} completes after emitting a single item that matches a given - * predicate, return that item, otherwise return a default value. - *

- * - * - * @param source - * the source {@link Observable} - * @param defaultValue - * a default value to return if the {@link Observable} emits no matching items - * @param predicate - * a predicate function to evaluate items emitted by the {@link Observable} - * @return the single item emitted by the source {@link Observable} that matches the predicate, - * or a default value if no such value is emitted - */ - public static T singleOrDefault(Observable source, T defaultValue, Object predicate) { - return from(source).singleOrDefault(defaultValue, predicate); - } - /** * Returns a {@link Future} representing the single value emitted by an {@link Observable}. *

@@ -476,45 +394,6 @@ public void onNext(T args) { } } - /** - * Invoke a method on each item emitted by the {@link Observable}; block until the Observable - * completes. - *

- * NOTE: This will block even if the Observable is asynchronous. - *

- * This is similar to {@link #subscribe(Observer)}, but it blocks. Because it blocks it does - * not need the {@link Observer#onCompleted()} or {@link Observer#onError(Throwable)} methods. - *

- * - * - * @param o - * the {@link Action1} to invoke for every item emitted by the {@link Observable} - * @throws RuntimeException - * if an error occurs - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void forEach(final Object o) { - if (o instanceof Action1) { - // in case a dynamic language is not correctly handling the overloaded methods and we receive an Action1 just forward to the correct method. - forEach((Action1) o); - } - - // lookup and memoize onNext - if (o == null) { - throw new RuntimeException("onNext must be implemented"); - } - final FuncN onNext = Functions.from(o); - - forEach(new Action1() { - - @Override - public void call(Object args) { - onNext.call(args); - } - - }); - } - /** * Returns an {@link Iterator} that iterates over all items emitted by a specified * {@link Observable}. @@ -555,27 +434,6 @@ public T last(final Func1 predicate) { return last(this, predicate); } - /** - * Returns the last item emitted by a specified {@link Observable} that matches a predicate. - *

- * - * - * @param predicate - * a predicate function to evaluate items emitted by the {@link Observable} - * @return the last item emitted by the {@link Observable} that matches the predicate - */ - public T last(final Object predicate) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(predicate); - - return last(this, new Func1() { - @Override - public Boolean call(T args) { - return (Boolean) _f.call(args); - } - }); - } - /** * Returns the last item emitted by a specified {@link Observable}, or a default value if no * items are emitted. @@ -620,23 +478,6 @@ public T lastOrDefault(T defaultValue, Func1 predicate) { return lastOrDefault(this, defaultValue, predicate); } - /** - * Returns the last item emitted by a specified {@link Observable} that matches a predicate, or - * a default value if no such items are emitted. - *

- * - * - * @param defaultValue - * a default value to return if the {@link Observable} emits no matching items - * @param predicate - * a predicate function to evaluate items emitted by the {@link Observable} - * @return the last item emitted by the {@link Observable} that matches the predicate, or the - * default value if no matching items are emitted - */ - public T lastOrDefault(T defaultValue, Object predicate) { - return lastOrDefault(this, defaultValue, predicate); - } - /** * Returns an {@link Iterable} that always returns the item most recently emitted by an * {@link Observable}. @@ -692,28 +533,6 @@ public T single(Func1 predicate) { return _singleOrDefault(from(this.filter(predicate)), false, null); } - /** - * If the {@link Observable} completes after emitting a single item that matches a given - * predicate, return that item, otherwise throw an exception. - *

- * - * - * @param predicate - * a predicate function to evaluate items emitted by the {@link Observable} - * @return the single item emitted by the source {@link Observable} that matches the predicate - */ - public T single(Object predicate) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(predicate); - - return single(new Func1() { - @Override - public Boolean call(T t) { - return (Boolean) _f.call(t); - } - }); - } - /** * If the {@link Observable} completes after emitting a single item, return that item; if it * emits more than one item, throw an exception; if it emits no items, return a default value. @@ -747,32 +566,6 @@ public T singleOrDefault(T defaultValue, Func1 predicate) { return _singleOrDefault(from(this.filter(predicate)), true, defaultValue); } - /** - * If the {@link Observable} completes after emitting a single item that matches a predicate, - * return that item; if it emits more than one such item, throw an exception; if it emits no - * items, return a default value. - *

- * - * - * @param defaultValue - * a default value to return if the {@link Observable} emits no matching items - * @param predicate - * a predicate function to evaluate items emitted by the {@link Observable} - * @return the single item emitted by the {@link Observable} that matches the predicate, or the - * default value if no such items are emitted - */ - public T singleOrDefault(T defaultValue, final Object predicate) { - @SuppressWarnings("rawtypes") - final FuncN _f = Functions.from(predicate); - - return singleOrDefault(defaultValue, new Func1() { - @Override - public Boolean call(T t) { - return (Boolean) _f.call(t); - } - }); - } - /** * Returns a {@link Future} representing the single value emitted by an {@link Observable}. *

diff --git a/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java b/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java index 089a850e99..f11af77898 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java +++ b/rxjava-core/src/main/java/rx/operators/OperationCombineLatest.java @@ -62,7 +62,7 @@ public class OperationCombineLatest { * The aggregation function used to combine the source observable values. * @return A function from an observer to a subscription. This can be used to create an observable from. */ - public static Func1, Subscription> combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) { + public static Func1, Subscription> combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); a.addObserver(new CombineObserver(a, w0)); a.addObserver(new CombineObserver(a, w1)); @@ -72,7 +72,7 @@ public static Func1, Subscription> combineLatest /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ - public static Func1, Subscription> combineLatest(Observable w0, Observable w1, Observable w2, Func3 combineLatestFunction) { + public static Func1, Subscription> combineLatest(Observable w0, Observable w1, Observable w2, Func3 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); a.addObserver(new CombineObserver(a, w0)); a.addObserver(new CombineObserver(a, w1)); @@ -83,7 +83,7 @@ public static Func1, Subscription> combineLa /** * @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction) */ - public static Func1, Subscription> combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Func4 combineLatestFunction) { + public static Func1, Subscription> combineLatest(Observable w0, Observable w1, Observable w2, Observable w3, Func4 combineLatestFunction) { Aggregator a = new Aggregator(Functions.fromFunc(combineLatestFunction)); a.addObserver(new CombineObserver(a, w0)); a.addObserver(new CombineObserver(a, w1)); @@ -93,11 +93,11 @@ public static Func1, Subscription> combi } private static class CombineObserver implements Observer { - final Observable w; - final Aggregator a; + final Observable w; + final Aggregator a; private Subscription subscription; - public CombineObserver(Aggregator a, Observable w) { + public CombineObserver(Aggregator a, Observable w) { this.a = a; this.w = w; } @@ -130,9 +130,9 @@ public void onNext(T args) { * whenever we have received an event from one of the observables, as soon as each Observable has received * at least one event. */ - private static class Aggregator implements Func1, Subscription> { + private static class Aggregator implements Func1, Subscription> { - private volatile Observer observer; + private volatile Observer observer; private final FuncN combineLatestFunction; private final AtomicBoolean running = new AtomicBoolean(true); @@ -169,7 +169,7 @@ void addObserver(CombineObserver w) { * * @param w The observer that has completed. */ - void complete(CombineObserver w) { + void complete(CombineObserver w) { int completed = numCompleted.incrementAndGet(); // if all CombineObservers are completed, we mark the whole thing as completed if (completed == observers.size()) { @@ -199,7 +199,7 @@ void error(Throwable e) { * @param w * @param arg */ - void next(CombineObserver w, T arg) { + void next(CombineObserver w, T arg) { if (observer == null) { throw new RuntimeException("This shouldn't be running if an Observer isn't registered"); } @@ -232,7 +232,7 @@ void next(CombineObserver w, T arg) { } @Override - public Subscription call(Observer observer) { + public Subscription call(Observer observer) { if (this.observer != null) { throw new IllegalStateException("Only one Observer can subscribe to this Observable."); } diff --git a/rxjava-core/src/main/java/rx/operators/OperationDematerialize.java b/rxjava-core/src/main/java/rx/operators/OperationDematerialize.java index 3d5c2d8392..14ac8748b6 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationDematerialize.java +++ b/rxjava-core/src/main/java/rx/operators/OperationDematerialize.java @@ -92,7 +92,7 @@ public static class UnitTest { @SuppressWarnings("unchecked") public void testDematerialize1() { Observable> notifications = Observable.from(1, 2).materialize(); - Observable dematerialize = Observable.dematerialize(notifications); + Observable dematerialize = notifications.dematerialize(); Observer aObserver = mock(Observer.class); dematerialize.subscribe(aObserver); diff --git a/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java b/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java index 8c0342a37f..5aa6ff6f68 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java +++ b/rxjava-core/src/main/java/rx/operators/OperationSubscribeOn.java @@ -16,6 +16,8 @@ package rx.operators; import org.junit.Test; +import org.mockito.Matchers; + import rx.Observable; import rx.Observer; import rx.Scheduler; @@ -24,6 +26,7 @@ import rx.util.functions.Action0; import rx.util.functions.Func0; import rx.util.functions.Func1; +import rx.util.functions.Func2; import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; @@ -50,9 +53,9 @@ public SubscribeOn(Observable source, Scheduler scheduler) { @Override public Subscription call(final Observer observer) { - return scheduler.schedule(new Func0() { + return scheduler.schedule(null, new Func2() { @Override - public Subscription call() { + public Subscription call(Scheduler s, T t) { return new ScheduledSubscription(source.subscribe(observer), scheduler); } }); @@ -91,7 +94,7 @@ public void testSubscribeOn() { Observer observer = mock(Observer.class); Subscription subscription = Observable.create(subscribeOn(w, scheduler)).subscribe(observer); - verify(scheduler, times(1)).schedule(any(Func0.class)); + verify(scheduler, times(1)).schedule(isNull(), any(Func2.class)); subscription.unsubscribe(); verify(scheduler, times(1)).schedule(any(Action0.class)); verifyNoMoreInteractions(scheduler); diff --git a/rxjava-core/src/main/java/rx/operators/OperationToIterator.java b/rxjava-core/src/main/java/rx/operators/OperationToIterator.java index f3c1b21067..4b56e7dda1 100644 --- a/rxjava-core/src/main/java/rx/operators/OperationToIterator.java +++ b/rxjava-core/src/main/java/rx/operators/OperationToIterator.java @@ -49,10 +49,10 @@ public class OperationToIterator { * the type of source. * @return the iterator that could be used to iterate over the elements of the observable. */ - public static Iterator toIterator(Observable that) { + public static Iterator toIterator(Observable source) { final BlockingQueue> notifications = new LinkedBlockingQueue>(); - Observable.materialize(that).subscribe(new Observer>() { + source.materialize().subscribe(new Observer>() { @Override public void onCompleted() { // ignore diff --git a/rxjava-core/src/main/java/rx/operators/OperationWhere.java b/rxjava-core/src/main/java/rx/operators/OperationWhere.java deleted file mode 100644 index d82b6d829b..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperationWhere.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.operators; - -import org.junit.Test; -import org.mockito.Mockito; - -import rx.Observable; -import rx.Observer; -import rx.Subscription; -import rx.util.functions.Func1; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -/** - * Filters an Observable by discarding any items it emits that do not meet some test. - *

- * - */ -public final class OperationWhere { - - public static Func1, Subscription> where(Observable that, Func1 predicate) { - return OperationFilter.filter(that, predicate); - } - - public static class UnitTest { - - @Test - public void testWhere() { - Observable w = Observable.from("one", "two", "three"); - Observable observable = Observable.create(where(w, new Func1() { - - @Override - public Boolean call(String t1) { - return t1.equals("two"); - } - })); - - @SuppressWarnings("unchecked") - Observer aObserver = mock(Observer.class); - observable.subscribe(aObserver); - verify(aObserver, Mockito.never()).onNext("one"); - verify(aObserver, times(1)).onNext("two"); - verify(aObserver, Mockito.never()).onNext("three"); - verify(aObserver, Mockito.never()).onError(any(Throwable.class)); - verify(aObserver, times(1)).onCompleted(); - } - } - -} diff --git a/rxjava-core/src/main/java/rx/operators/OperatorTester.java b/rxjava-core/src/main/java/rx/operators/OperatorTester.java index 22283673d5..664dd37cc8 100644 --- a/rxjava-core/src/main/java/rx/operators/OperatorTester.java +++ b/rxjava-core/src/main/java/rx/operators/OperatorTester.java @@ -15,25 +15,11 @@ */ package rx.operators; -import static org.junit.Assert.*; - -import java.lang.Thread.UncaughtExceptionHandler; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.Test; -import rx.Observable; -import rx.Observer; import rx.Scheduler; import rx.Subscription; -import rx.subscriptions.Subscriptions; import rx.util.functions.Action0; -import rx.util.functions.Func0; -import rx.util.functions.Func1; import rx.util.functions.Func2; /** @@ -77,16 +63,6 @@ public Subscription schedule(Action0 action) { return underlying.schedule(action); } - @Override - public Subscription schedule(Func0 action) { - return underlying.schedule(action); - } - - @Override - public Subscription schedule(Func1 action) { - return underlying.schedule(action); - } - @Override public Subscription schedule(T state, Func2 action) { return underlying.schedule(state, action); @@ -97,16 +73,6 @@ public Subscription schedule(Action0 action, long dueTime, TimeUnit unit) { return underlying.schedule(action, dueTime, unit); } - @Override - public Subscription schedule(Func0 action, long dueTime, TimeUnit unit) { - return underlying.schedule(action, dueTime, unit); - } - - @Override - public Subscription schedule(Func1 action, long dueTime, TimeUnit unit) { - return underlying.schedule(action, dueTime, unit); - } - @Override public Subscription schedule(T state, Func2 action, long dueTime, TimeUnit unit) { return underlying.schedule(state, action, dueTime, unit); @@ -117,16 +83,6 @@ public Subscription schedulePeriodically(Action0 action, long initialDelay, long return underlying.schedulePeriodically(action, initialDelay, period, unit); } - @Override - public Subscription schedulePeriodically(Func0 action, long initialDelay, long period, TimeUnit unit) { - return underlying.schedulePeriodically(action, initialDelay, period, unit); - } - - @Override - public Subscription schedulePeriodically(Func1 action, long initialDelay, long period, TimeUnit unit) { - return underlying.schedulePeriodically(action, initialDelay, period, unit); - } - @Override public Subscription schedulePeriodically(T state, Func2 action, long initialDelay, long period, TimeUnit unit) { return underlying.schedulePeriodically(state, action, initialDelay, period, unit); diff --git a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java index b154702ff8..c25ea56dee 100644 --- a/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/AsyncSubject.java @@ -130,9 +130,8 @@ public static class UnitTest { @Test public void testNeverCompleted() { - AsyncSubject subject = AsyncSubject.create(); + AsyncSubject subject = AsyncSubject.create(); - @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); subject.subscribe(aObserver); @@ -152,9 +151,8 @@ private void assertNeverCompletedObserver(Observer aObserver) @Test public void testCompleted() { - AsyncSubject subject = AsyncSubject.create(); + AsyncSubject subject = AsyncSubject.create(); - @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); subject.subscribe(aObserver); @@ -175,9 +173,8 @@ private void assertCompletedObserver(Observer aObserver) @Test public void testError() { - AsyncSubject subject = AsyncSubject.create(); + AsyncSubject subject = AsyncSubject.create(); - @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); subject.subscribe(aObserver); @@ -201,7 +198,7 @@ private void assertErrorObserver(Observer aObserver) @Test public void testUnsubscribeBeforeCompleted() { - AsyncSubject subject = AsyncSubject.create(); + AsyncSubject subject = AsyncSubject.create(); @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); diff --git a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java index 5fccdb86d2..467771758d 100644 --- a/rxjava-core/src/main/java/rx/subjects/PublishSubject.java +++ b/rxjava-core/src/main/java/rx/subjects/PublishSubject.java @@ -195,12 +195,12 @@ public static class UnitTest { @Test public void test() { PublishSubject subject = PublishSubject.create(); - final AtomicReference>> actualRef = new AtomicReference>>(); + final AtomicReference>> actualRef = new AtomicReference>>(); Observable>> wNotificationsList = subject.materialize().toList(); - wNotificationsList.subscribe(new Action1>>() { + wNotificationsList.subscribe(new Action1>>() { @Override - public void call(List> actual) { + public void call(List> actual) { actualRef.set(actual); } }); @@ -245,9 +245,8 @@ public void unsubscribe() { @Test public void testCompleted() { - PublishSubject subject = PublishSubject.create(); + PublishSubject subject = PublishSubject.create(); - @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); subject.subscribe(aObserver); @@ -256,7 +255,6 @@ public void testCompleted() { subject.onNext("three"); subject.onCompleted(); - @SuppressWarnings("unchecked") Observer anotherObserver = mock(Observer.class); subject.subscribe(anotherObserver); @@ -286,9 +284,8 @@ private void assertNeverObserver(Observer aObserver) @Test public void testError() { - PublishSubject subject = PublishSubject.create(); + PublishSubject subject = PublishSubject.create(); - @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); subject.subscribe(aObserver); @@ -297,7 +294,6 @@ public void testError() { subject.onNext("three"); subject.onError(testException); - @SuppressWarnings("unchecked") Observer anotherObserver = mock(Observer.class); subject.subscribe(anotherObserver); @@ -320,9 +316,8 @@ private void assertErrorObserver(Observer aObserver) @Test public void testSubscribeMidSequence() { - PublishSubject subject = PublishSubject.create(); + PublishSubject subject = PublishSubject.create(); - @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); subject.subscribe(aObserver); @@ -331,7 +326,6 @@ public void testSubscribeMidSequence() { assertObservedUntilTwo(aObserver); - @SuppressWarnings("unchecked") Observer anotherObserver = mock(Observer.class); subject.subscribe(anotherObserver); @@ -353,9 +347,8 @@ private void assertCompletedStartingWithThreeObserver(Observer aObserver @Test public void testUnsubscribeFirstObserver() { - PublishSubject subject = PublishSubject.create(); + PublishSubject subject = PublishSubject.create(); - @SuppressWarnings("unchecked") Observer aObserver = mock(Observer.class); Subscription subscription = subject.subscribe(aObserver); @@ -365,7 +358,6 @@ public void testUnsubscribeFirstObserver() { subscription.unsubscribe(); assertObservedUntilTwo(aObserver); - @SuppressWarnings("unchecked") Observer anotherObserver = mock(Observer.class); subject.subscribe(anotherObserver); @@ -397,9 +389,8 @@ private void assertObservedUntilTwo(Observer aObserver) */ @Test public void testUnsubscribeAfterOnCompleted() { - PublishSubject subject = PublishSubject.create(); + PublishSubject subject = PublishSubject.create(); - @SuppressWarnings("unchecked") Observer anObserver = mock(Observer.class); subject.subscribe(anObserver); @@ -426,7 +417,7 @@ public void testUnsubscribeAfterOnCompleted() { @Test public void testUnsubscribeAfterOnError() { - PublishSubject subject = PublishSubject.create(); + PublishSubject subject = PublishSubject.create(); RuntimeException exception = new RuntimeException("failure"); @SuppressWarnings("unchecked") diff --git a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java index b6711c15c4..2d852f5bb0 100644 --- a/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java +++ b/rxjava-core/src/main/java/rx/subjects/ReplaySubject.java @@ -186,7 +186,7 @@ public static class UnitTest { @SuppressWarnings("unchecked") @Test public void testCompleted() { - ReplaySubject subject = ReplaySubject.create(); + ReplaySubject subject = ReplaySubject.create(); Observer o1 = mock(Observer.class); subject.subscribe(o1); @@ -223,7 +223,7 @@ private void assertCompletedObserver(Observer aObserver) @SuppressWarnings("unchecked") @Test public void testError() { - ReplaySubject subject = ReplaySubject.create(); + ReplaySubject subject = ReplaySubject.create(); Observer aObserver = mock(Observer.class); subject.subscribe(aObserver); @@ -256,7 +256,7 @@ private void assertErrorObserver(Observer aObserver) @SuppressWarnings("unchecked") @Test public void testSubscribeMidSequence() { - ReplaySubject subject = ReplaySubject.create(); + ReplaySubject subject = ReplaySubject.create(); Observer aObserver = mock(Observer.class); subject.subscribe(aObserver); @@ -280,7 +280,7 @@ public void testSubscribeMidSequence() { @SuppressWarnings("unchecked") @Test public void testUnsubscribeFirstObserver() { - ReplaySubject subject = ReplaySubject.create(); + ReplaySubject subject = ReplaySubject.create(); Observer aObserver = mock(Observer.class); Subscription subscription = subject.subscribe(aObserver); diff --git a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java index f3f1dd46c7..788942e99a 100644 --- a/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java +++ b/rxjava-core/src/main/java/rx/subscriptions/Subscriptions.java @@ -20,6 +20,7 @@ import rx.Subscription; import rx.util.functions.Action0; import rx.util.functions.FuncN; +import rx.util.functions.Function; import rx.util.functions.Functions; /** @@ -83,23 +84,6 @@ public static CompositeSubscription create(Subscription... subscriptions) { return new CompositeSubscription(subscriptions); } - /** - * A {@link Subscription} implemented via an anonymous function (such as closures from other languages). - * - * @return {@link Subscription} - */ - public static Subscription create(final Object unsubscribe) { - final FuncN f = Functions.from(unsubscribe); - return new Subscription() { - - @Override - public void unsubscribe() { - f.call(); - } - - }; - } - /** * A {@link Subscription} that does nothing when its unsubscribe method is called. */ diff --git a/rxjava-core/src/main/java/rx/util/functions/Action.java b/rxjava-core/src/main/java/rx/util/functions/Action.java new file mode 100644 index 0000000000..27d781e957 --- /dev/null +++ b/rxjava-core/src/main/java/rx/util/functions/Action.java @@ -0,0 +1,10 @@ +package rx.util.functions; + +/** + * All Action interfaces extend from this. + *

+ * Marker interface to allow instanceof checks. + */ +public interface Action extends Function { + +} diff --git a/rxjava-core/src/main/java/rx/util/functions/Action0.java b/rxjava-core/src/main/java/rx/util/functions/Action0.java index 62d57bd563..7b6e742699 100644 --- a/rxjava-core/src/main/java/rx/util/functions/Action0.java +++ b/rxjava-core/src/main/java/rx/util/functions/Action0.java @@ -15,6 +15,6 @@ */ package rx.util.functions; -public interface Action0 extends Function { +public interface Action0 extends Function, Action { public void call(); } \ No newline at end of file diff --git a/rxjava-core/src/main/java/rx/util/functions/Action1.java b/rxjava-core/src/main/java/rx/util/functions/Action1.java index 14fa7ced8c..e21fd4e38b 100644 --- a/rxjava-core/src/main/java/rx/util/functions/Action1.java +++ b/rxjava-core/src/main/java/rx/util/functions/Action1.java @@ -15,6 +15,6 @@ */ package rx.util.functions; -public interface Action1 extends Function { +public interface Action1 extends Function, Action { public void call(T1 t1); } \ No newline at end of file diff --git a/rxjava-core/src/main/java/rx/util/functions/Action2.java b/rxjava-core/src/main/java/rx/util/functions/Action2.java index 8a17875a9e..76c48d3eaf 100644 --- a/rxjava-core/src/main/java/rx/util/functions/Action2.java +++ b/rxjava-core/src/main/java/rx/util/functions/Action2.java @@ -15,6 +15,6 @@ */ package rx.util.functions; -public interface Action2 extends Function { +public interface Action2 extends Function, Action { public void call(T1 t1, T2 t2); } \ No newline at end of file diff --git a/rxjava-core/src/main/java/rx/util/functions/Action3.java b/rxjava-core/src/main/java/rx/util/functions/Action3.java index 2b613b621e..0bb6932792 100644 --- a/rxjava-core/src/main/java/rx/util/functions/Action3.java +++ b/rxjava-core/src/main/java/rx/util/functions/Action3.java @@ -15,6 +15,6 @@ */ package rx.util.functions; -public interface Action3 extends Function { +public interface Action3 extends Function, Action { public void call(T1 t1, T2 t2, T3 t3); } \ No newline at end of file diff --git a/rxjava-core/src/main/java/rx/util/functions/Function.java b/rxjava-core/src/main/java/rx/util/functions/Function.java index cfe85a221f..60ad4b5239 100644 --- a/rxjava-core/src/main/java/rx/util/functions/Function.java +++ b/rxjava-core/src/main/java/rx/util/functions/Function.java @@ -3,7 +3,7 @@ /** * All Func and Action interfaces extend from this. *

- * Marker interface to allow isntanceof checks. + * Marker interface to allow instanceof checks. */ public interface Function { diff --git a/rxjava-core/src/main/java/rx/util/functions/FunctionLanguageAdaptor.java b/rxjava-core/src/main/java/rx/util/functions/FunctionLanguageAdaptor.java deleted file mode 100644 index 6ec87f358a..0000000000 --- a/rxjava-core/src/main/java/rx/util/functions/FunctionLanguageAdaptor.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2013 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.util.functions; - -public interface FunctionLanguageAdaptor { - - /** - * Invoke the function and return the results. - * - * @param function - * @param args - * @return Object results from function execution - */ - Object call(Object function, Object[] args); - - /** - * The Class of the Function that this adaptor serves. - *

- * Example: groovy.lang.Closure - *

- * This should not return classes of java.* packages. - * - * @return Class[] of classes that this adaptor should be invoked for. - */ - public Class[] getFunctionClass(); -} diff --git a/rxjava-core/src/main/java/rx/util/functions/Functions.java b/rxjava-core/src/main/java/rx/util/functions/Functions.java index 4486e7bd26..c66ca837e4 100644 --- a/rxjava-core/src/main/java/rx/util/functions/Functions.java +++ b/rxjava-core/src/main/java/rx/util/functions/Functions.java @@ -15,207 +15,21 @@ */ package rx.util.functions; -import java.util.Collection; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Allows execution of functions from multiple different languages. - *

- * Language support is provided via implementations of {@link FunctionLanguageAdaptor}. - *

- * This class will dynamically look for known language adaptors on the classpath at startup or new ones can be registered using {@link #registerLanguageAdaptor(Class[], FunctionLanguageAdaptor)}. - */ public class Functions { - private final static ConcurrentHashMap, FunctionLanguageAdaptor> languageAdaptors = new ConcurrentHashMap, FunctionLanguageAdaptor>(); - - static { - /* optimistically look for supported languages if they are in the classpath */ - loadLanguageAdaptor("Groovy"); - loadLanguageAdaptor("JRuby"); - loadLanguageAdaptor("Clojure"); - loadLanguageAdaptor("Scala"); - // as new languages arise we can add them here but this does not prevent someone from using 'registerLanguageAdaptor' directly - } - - private static boolean loadLanguageAdaptor(String name) { - String className = "rx.lang." + name.toLowerCase() + "." + name + "Adaptor"; - try { - Class c = Class.forName(className); - FunctionLanguageAdaptor a = (FunctionLanguageAdaptor) c.newInstance(); - registerLanguageAdaptor(a.getFunctionClass(), a); - /* - * Using System.err/System.out as this is the only place in the library where we do logging and it's only at startup. - * I don't want to include SL4J/Log4j just for this and no one uses Java Logging. - */ - System.out.println("RxJava => Successfully loaded function language adaptor: " + name + " with path: " + className); - } catch (ClassNotFoundException e) { - System.err.println("RxJava => Could not find function language adaptor: " + name + " with path: " + className); - return false; - } catch (Throwable e) { - System.err.println("RxJava => Failed trying to initialize function language adaptor: " + className); - e.printStackTrace(); - return false; - } - return true; - } - - public static void registerLanguageAdaptor(Class[] functionClasses, FunctionLanguageAdaptor adaptor) { - for (Class functionClass : functionClasses) { - if (functionClass.getPackage().getName().startsWith("java.")) { - throw new IllegalArgumentException("FunctionLanguageAdaptor implementations can not specify java.lang.* classes."); - } - languageAdaptors.put(functionClass, adaptor); - } - } - - public static void removeLanguageAdaptor(Class functionClass) { - languageAdaptors.remove(functionClass); - } - - public static Collection getRegisteredLanguageAdaptors() { - return languageAdaptors.values(); - } - /** * Utility method for determining the type of closure/function and executing it. * * @param function */ @SuppressWarnings({ "rawtypes" }) - public static FuncN from(final Object function) { + public static FuncN from(final Function function) { if (function == null) { throw new RuntimeException("function is null. Can't send arguments to null function."); } - - /* check for typed Rx Function implementation first */ - if (function instanceof Function) { - return fromFunction((Function) function); - } else { - /* not an Rx Function so try language adaptors */ - - // check for language adaptor - for (final Class c : languageAdaptors.keySet()) { - if (c.isInstance(function)) { - final FunctionLanguageAdaptor la = languageAdaptors.get(c); - // found the language adaptor so wrap in FuncN and return - return new FuncN() { - - @Override - public Object call(Object... args) { - return la.call(function, args); - } - - }; - } - } - // no language adaptor found - } - - // no support found - throw new RuntimeException("Unsupported closure type: " + function.getClass().getSimpleName()); + return fromFunction(function); } - // - // @SuppressWarnings("unchecked") - // private static R executionRxFunction(Function function, Object... args) { - // // check Func* classes - // if (function instanceof Func0) { - // Func0 f = (Func0) function; - // if (args.length != 0) { - // throw new RuntimeException("The closure was Func0 and expected no arguments, but we received: " + args.length); - // } - // return (R) f.call(); - // } else if (function instanceof Func1) { - // Func1 f = (Func1) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Func1 and expected 1 argument, but we received: " + args.length); - // } - // return f.call(args[0]); - // } else if (function instanceof Func2) { - // Func2 f = (Func2) function; - // if (args.length != 2) { - // throw new RuntimeException("The closure was Func2 and expected 2 arguments, but we received: " + args.length); - // } - // return f.call(args[0], args[1]); - // } else if (function instanceof Func3) { - // Func3 f = (Func3) function; - // if (args.length != 3) { - // throw new RuntimeException("The closure was Func3 and expected 3 arguments, but we received: " + args.length); - // } - // return (R) f.call(args[0], args[1], args[2]); - // } else if (function instanceof Func4) { - // Func4 f = (Func4) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Func4 and expected 4 arguments, but we received: " + args.length); - // } - // return f.call(args[0], args[1], args[2], args[3]); - // } else if (function instanceof Func5) { - // Func5 f = (Func5) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Func5 and expected 5 arguments, but we received: " + args.length); - // } - // return f.call(args[0], args[1], args[2], args[3], args[4]); - // } else if (function instanceof Func6) { - // Func6 f = (Func6) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Func6 and expected 6 arguments, but we received: " + args.length); - // } - // return f.call(args[0], args[1], args[2], args[3], args[4], args[5]); - // } else if (function instanceof Func7) { - // Func7 f = (Func7) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Func7 and expected 7 arguments, but we received: " + args.length); - // } - // return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); - // } else if (function instanceof Func8) { - // Func8 f = (Func8) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Func8 and expected 8 arguments, but we received: " + args.length); - // } - // return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); - // } else if (function instanceof Func9) { - // Func9 f = (Func9) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Func9 and expected 9 arguments, but we received: " + args.length); - // } - // return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); - // } else if (function instanceof FuncN) { - // FuncN f = (FuncN) function; - // return f.call(args); - // } else if (function instanceof Action0) { - // Action0 f = (Action0) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Action0 and expected 0 arguments, but we received: " + args.length); - // } - // f.call(); - // return null; - // } else if (function instanceof Action1) { - // Action1 f = (Action1) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Action1 and expected 1 argument, but we received: " + args.length); - // } - // f.call(args[0]); - // return null; - // } else if (function instanceof Action2) { - // Action2 f = (Action2) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Action2 and expected 2 argument, but we received: " + args.length); - // } - // f.call(args[0], args[1]); - // return null; - // } else if (function instanceof Action3) { - // Action3 f = (Action3) function; - // if (args.length != 1) { - // throw new RuntimeException("The closure was Action1 and expected 1 argument, but we received: " + args.length); - // } - // f.call(args[0], args[1], args[2]); - // return null; - // } - // - // throw new RuntimeException("Unknown implementation of Function: " + function.getClass().getSimpleName()); - // } - @SuppressWarnings({ "unchecked", "rawtypes" }) private static FuncN fromFunction(Function function) { // check Func* classes diff --git a/rxjava-core/src/test/java/README.md b/rxjava-core/src/test/java/README.md index 74b6c91536..c6a9aea6af 100644 --- a/rxjava-core/src/test/java/README.md +++ b/rxjava-core/src/test/java/README.md @@ -1,6 +1,4 @@ -This test folder only contains performance and functional/integration style tests. - -The unit tests themselves are embedded as inner classes of the Java code (such as here: [rxjava-core/src/main/java/rx/operators](https://github.com/Netflix/RxJava/tree/master/rxjava-core/src/main/java/rx/operators)). +Not all unit tests are here, many are also embedded as inner classes of the main code (such as here: [rxjava-core/src/main/java/rx/operators](https://github.com/Netflix/RxJava/tree/master/rxjava-core/src/main/java/rx/operators)). * For an explanation of this design choice see Ben J. Christensen's [JUnit Tests as Inner Classes](http://benjchristensen.com/2011/10/23/junit-tests-as-inner-classes/). diff --git a/rxjava-core/src/test/java/rx/ObservableTests.java b/rxjava-core/src/test/java/rx/ObservableTests.java new file mode 100644 index 0000000000..690d45859e --- /dev/null +++ b/rxjava-core/src/test/java/rx/ObservableTests.java @@ -0,0 +1,536 @@ +package rx; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +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.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import rx.observables.ConnectableObservable; +import rx.subscriptions.BooleanSubscription; +import rx.subscriptions.Subscriptions; +import rx.util.functions.Action1; +import rx.util.functions.Func1; +import rx.util.functions.Func2; + +public class ObservableTests { + + @Mock + Observer w; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testCreate() { + + Observable observable = Observable.create(new Func1, Subscription>() { + + @Override + public Subscription call(Observer Observer) { + Observer.onNext("one"); + Observer.onNext("two"); + Observer.onNext("three"); + Observer.onCompleted(); + return Subscriptions.empty(); + } + + }); + + @SuppressWarnings("unchecked") + Observer aObserver = mock(Observer.class); + observable.subscribe(aObserver); + verify(aObserver, times(1)).onNext("one"); + verify(aObserver, times(1)).onNext("two"); + verify(aObserver, times(1)).onNext("three"); + verify(aObserver, Mockito.never()).onError(any(Throwable.class)); + verify(aObserver, times(1)).onCompleted(); + } + + @Test + public void testReduce() { + Observable observable = Observable.from(1, 2, 3, 4); + observable.reduce(new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + + }).subscribe(w); + // we should be called only once + verify(w, times(1)).onNext(anyInt()); + verify(w).onNext(10); + } + + @Test + public void testReduceWithInitialValue() { + Observable observable = Observable.from(1, 2, 3, 4); + observable.reduce(50, new Func2() { + + @Override + public Integer call(Integer t1, Integer t2) { + return t1 + t2; + } + + }).subscribe(w); + // we should be called only once + verify(w, times(1)).onNext(anyInt()); + verify(w).onNext(60); + } + + @Test + public void testSequenceEqual() { + Observable first = Observable.from(1, 2, 3); + Observable second = Observable.from(1, 2, 4); + @SuppressWarnings("unchecked") + Observer result = mock(Observer.class); + Observable.sequenceEqual(first, second).subscribe(result); + verify(result, times(2)).onNext(true); + verify(result, times(1)).onNext(false); + } + + @Test + public void testOnSubscribeFails() { + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + final RuntimeException re = new RuntimeException("bad impl"); + Observable o = Observable.create(new Func1, Subscription>() { + + @Override + public Subscription call(Observer t1) { + throw re; + } + + }); + o.subscribe(observer); + verify(observer, times(0)).onNext(anyString()); + verify(observer, times(0)).onCompleted(); + verify(observer, times(1)).onError(re); + } + + @Test + public void testMaterializeDematerializeChaining() { + Observable obs = Observable.just(1); + Observable chained = obs.materialize().dematerialize(); + + @SuppressWarnings("unchecked") + Observer observer = mock(Observer.class); + chained.subscribe(observer); + + verify(observer, times(1)).onNext(1); + verify(observer, times(1)).onCompleted(); + verify(observer, times(0)).onError(any(Throwable.class)); + } + + /** + * The error from the user provided Observer is not handled by the subscribe method try/catch. + * + * It is handled by the AtomicObserver that wraps the provided Observer. + * + * Result: Passes (if AtomicObserver functionality exists) + */ + @Test + public void testCustomObservableWithErrorInObserverAsynchronous() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicInteger count = new AtomicInteger(); + final AtomicReference error = new AtomicReference(); + Observable.create(new Func1, Subscription>() { + + @Override + public Subscription call(final Observer observer) { + final BooleanSubscription s = new BooleanSubscription(); + new Thread(new Runnable() { + + @Override + public void run() { + try { + if (!s.isUnsubscribed()) { + observer.onNext("1"); + observer.onNext("2"); + observer.onNext("three"); + observer.onNext("4"); + observer.onCompleted(); + } + } finally { + latch.countDown(); + } + } + }).start(); + return s; + } + }).subscribe(new Observer() { + @Override + public void onCompleted() { + System.out.println("completed"); + } + + @Override + public void onError(Throwable e) { + error.set(e); + System.out.println("error"); + e.printStackTrace(); + } + + @Override + public void onNext(String v) { + int num = Integer.parseInt(v); + System.out.println(num); + // doSomething(num); + count.incrementAndGet(); + } + + }); + + // wait for async sequence to complete + latch.await(); + + assertEquals(2, count.get()); + assertNotNull(error.get()); + if (!(error.get() instanceof NumberFormatException)) { + fail("It should be a NumberFormatException"); + } + } + + /** + * The error from the user provided Observer is handled by the subscribe try/catch because this is synchronous + * + * Result: Passes + */ + @Test + public void testCustomObservableWithErrorInObserverSynchronous() { + final AtomicInteger count = new AtomicInteger(); + final AtomicReference error = new AtomicReference(); + Observable.create(new Func1, Subscription>() { + + @Override + public Subscription call(Observer observer) { + observer.onNext("1"); + observer.onNext("2"); + observer.onNext("three"); + observer.onNext("4"); + observer.onCompleted(); + return Subscriptions.empty(); + } + }).subscribe(new Observer() { + + @Override + public void onCompleted() { + System.out.println("completed"); + } + + @Override + public void onError(Throwable e) { + error.set(e); + System.out.println("error"); + e.printStackTrace(); + } + + @Override + public void onNext(String v) { + int num = Integer.parseInt(v); + System.out.println(num); + // doSomething(num); + count.incrementAndGet(); + } + + }); + assertEquals(2, count.get()); + assertNotNull(error.get()); + if (!(error.get() instanceof NumberFormatException)) { + fail("It should be a NumberFormatException"); + } + } + + /** + * The error from the user provided Observable is handled by the subscribe try/catch because this is synchronous + * + * + * Result: Passes + */ + @Test + public void testCustomObservableWithErrorInObservableSynchronous() { + final AtomicInteger count = new AtomicInteger(); + final AtomicReference error = new AtomicReference(); + Observable.create(new Func1, Subscription>() { + + @Override + public Subscription call(Observer observer) { + observer.onNext("1"); + observer.onNext("2"); + throw new NumberFormatException(); + } + }).subscribe(new Observer() { + + @Override + public void onCompleted() { + System.out.println("completed"); + } + + @Override + public void onError(Throwable e) { + error.set(e); + System.out.println("error"); + e.printStackTrace(); + } + + @Override + public void onNext(String v) { + System.out.println(v); + count.incrementAndGet(); + } + + }); + assertEquals(2, count.get()); + assertNotNull(error.get()); + if (!(error.get() instanceof NumberFormatException)) { + fail("It should be a NumberFormatException"); + } + } + + @Test + public void testPublish() throws InterruptedException { + final AtomicInteger counter = new AtomicInteger(); + ConnectableObservable o = Observable.create(new Func1, Subscription>() { + + @Override + public Subscription call(final Observer observer) { + final BooleanSubscription subscription = new BooleanSubscription(); + new Thread(new Runnable() { + + @Override + public void run() { + counter.incrementAndGet(); + observer.onNext("one"); + observer.onCompleted(); + } + }).start(); + return subscription; + } + }).publish(); + + final CountDownLatch latch = new CountDownLatch(2); + + // subscribe once + o.subscribe(new Action1() { + + @Override + public void call(String v) { + assertEquals("one", v); + latch.countDown(); + } + }); + + // subscribe again + o.subscribe(new Action1() { + + @Override + public void call(String v) { + assertEquals("one", v); + latch.countDown(); + } + }); + + Subscription s = o.connect(); + try { + if (!latch.await(1000, TimeUnit.MILLISECONDS)) { + fail("subscriptions did not receive values"); + } + assertEquals(1, counter.get()); + } finally { + s.unsubscribe(); + } + } + + @Test + public void testReplay() throws InterruptedException { + final AtomicInteger counter = new AtomicInteger(); + ConnectableObservable o = Observable.create(new Func1, Subscription>() { + + @Override + public Subscription call(final Observer observer) { + final BooleanSubscription subscription = new BooleanSubscription(); + new Thread(new Runnable() { + + @Override + public void run() { + counter.incrementAndGet(); + observer.onNext("one"); + observer.onCompleted(); + } + }).start(); + return subscription; + } + }).replay(); + + // we connect immediately and it will emit the value + Subscription s = o.connect(); + try { + + // we then expect the following 2 subscriptions to get that same value + final CountDownLatch latch = new CountDownLatch(2); + + // subscribe once + o.subscribe(new Action1() { + + @Override + public void call(String v) { + assertEquals("one", v); + latch.countDown(); + } + }); + + // subscribe again + o.subscribe(new Action1() { + + @Override + public void call(String v) { + assertEquals("one", v); + latch.countDown(); + } + }); + + if (!latch.await(1000, TimeUnit.MILLISECONDS)) { + fail("subscriptions did not receive values"); + } + assertEquals(1, counter.get()); + } finally { + s.unsubscribe(); + } + } + + @Test + public void testCache() throws InterruptedException { + final AtomicInteger counter = new AtomicInteger(); + Observable o = Observable.create(new Func1, Subscription>() { + + @Override + public Subscription call(final Observer observer) { + final BooleanSubscription subscription = new BooleanSubscription(); + new Thread(new Runnable() { + + @Override + public void run() { + counter.incrementAndGet(); + observer.onNext("one"); + observer.onCompleted(); + } + }).start(); + return subscription; + } + }).cache(); + + // we then expect the following 2 subscriptions to get that same value + final CountDownLatch latch = new CountDownLatch(2); + + // subscribe once + o.subscribe(new Action1() { + + @Override + public void call(String v) { + assertEquals("one", v); + latch.countDown(); + } + }); + + // subscribe again + o.subscribe(new Action1() { + + @Override + public void call(String v) { + assertEquals("one", v); + latch.countDown(); + } + }); + + if (!latch.await(1000, TimeUnit.MILLISECONDS)) { + fail("subscriptions did not receive values"); + } + assertEquals(1, counter.get()); + } + + /** + * https://github.com/Netflix/RxJava/issues/198 + * + * Rx Design Guidelines 5.2 + * + * "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be + * to rethrow the exception on the thread that the message comes out from the Observable. + * The OnCompleted behavior in this case is to do nothing." + */ + @Test + public void testErrorThrownWithoutErrorHandlerSynchronous() { + try { + Observable.error(new RuntimeException("failure")).subscribe(new Action1() { + + @Override + public void call(Object t1) { + // won't get anything + } + + }); + fail("expected exception"); + } catch (Throwable e) { + assertEquals("failure", e.getMessage()); + } + } + + /** + * https://github.com/Netflix/RxJava/issues/198 + * + * Rx Design Guidelines 5.2 + * + * "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be + * to rethrow the exception on the thread that the message comes out from the Observable. + * The OnCompleted behavior in this case is to do nothing." + * + * @throws InterruptedException + */ + @Test + public void testErrorThrownWithoutErrorHandlerAsynchronous() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference exception = new AtomicReference(); + Observable.create(new Func1, Subscription>() { + + @Override + public Subscription call(final Observer observer) { + new Thread(new Runnable() { + + @Override + public void run() { + try { + observer.onError(new Error("failure")); + } catch (Throwable e) { + // without an onError handler it has to just throw on whatever thread invokes it + exception.set(e); + } + latch.countDown(); + } + }).start(); + return Subscriptions.empty(); + } + }).subscribe(new Action1() { + + @Override + public void call(String t1) { + + } + + }); + // wait for exception + latch.await(3000, TimeUnit.MILLISECONDS); + assertNotNull(exception.get()); + assertEquals("failure", exception.get().getMessage()); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 5d33717f7a..5c7140dc5c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,6 @@ rootProject.name='rxjava' include 'rxjava-core', \ 'language-adaptors:rxjava-groovy', \ -'language-adaptors:rxjava-jruby', \ 'language-adaptors:rxjava-clojure', \ 'language-adaptors:rxjava-scala', \ 'rxjava-contrib:rxjava-swing', \