diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..3412422e07 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +Thanks for using RxJava but before you post an issue, please consider the following points: + + - [ ] Please include the library version number, including the minor and patch version, in the issue text. In addition, if you'd include the major version in the title (such as `3.x`) that would be great. + + - [ ] If you think you found a bug, please include a code sample that reproduces the problem. Dumping a stacktrace is usually not enough for us. + + - [ ] RxJava has more than 150 operators, we recommend searching the [javadoc](http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/Observable.html) for keywords of what you try to accomplish. + + - [ ] If you have a question/issue about a library/technology built on top of RxJava (such as Retrofit, RxNetty, etc.), please consider asking a question on StackOverflow first (then maybe on their issue list). + + - [ ] Questions like "how do I X with RxJava" are generally better suited for StackOverflow (where it may already have an answer). + + - [ ] Please avoid cross-posting questions on StackOverflow, this issue list, the Gitter room or the mailing list. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..885315565f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +Thank you for contributing to RxJava. Before pressing the "Create Pull Request" button, please consider the following points: + + - [ ] Please give a description about what and why you are contributing, even if it's trivial. + + - [ ] Please include the issue list number(s) or other PR numbers in the description if you are contributing in response to those. + + - [ ] Please include a reasonable set of unit tests if you contribute new code or change an existing one. If you contribute an operator, (if applicable) please make sure you have tests for working with an `empty`, `just`, `range` of values as well as an `error` source, with and/or without backpressure and see if unsubscription/cancellation propagates correctly. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..def5ba5d90 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 0000000000..3e905f1c92 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,13 @@ +name: "Validate Gradle Wrapper" +on: [push, pull_request] + +permissions: + contents: read + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: gradle/wrapper-validation-action@f9c9c575b8b21b6485636a91ffecd10e558c62f6 # v3.5.0 diff --git a/.github/workflows/gradle_branch.yml b/.github/workflows/gradle_branch.yml new file mode 100644 index 0000000000..cb7231a7bf --- /dev/null +++ b/.github/workflows/gradle_branch.yml @@ -0,0 +1,37 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Branch + +on: + push: + branches-ignore: [ '3.x' ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Set up JDK 11 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'zulu' + java-version: '11' + - name: Cache Gradle packages + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }} + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build RxJava + run: ./gradlew build --stacktrace + - name: Upload to Codecov + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + - name: Generate Javadoc + run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_jdk11.yml b/.github/workflows/gradle_jdk11.yml new file mode 100644 index 0000000000..771a1afe0f --- /dev/null +++ b/.github/workflows/gradle_jdk11.yml @@ -0,0 +1,42 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: JDK 11 + +on: + push: + branches: [ 3.x ] + pull_request: + branches: [ 3.x ] + +permissions: + contents: read + +env: + BUILD_WITH_11: true + +jobs: + build: + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Set up JDK 11 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'zulu' + java-version: '11' + - name: Cache Gradle packages + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-1-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle-1- + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Verify generated module-info + run: ./gradlew -PjavaCompatibility=9 jar + - name: Build RxJava + run: ./gradlew build --stacktrace +# - name: Generate Javadoc +# run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_pr.yml b/.github/workflows/gradle_pr.yml new file mode 100644 index 0000000000..f76409cd46 --- /dev/null +++ b/.github/workflows/gradle_pr.yml @@ -0,0 +1,37 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Pull Request + +on: + pull_request: + branches: [ 3.x ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Set up JDK 11 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'zulu' + java-version: '11' + - name: Cache Gradle packages + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-1-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle-1- + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build RxJava + run: ./gradlew build --stacktrace + - name: Upload to Codecov + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + - name: Generate Javadoc + run: ./gradlew javadoc --stacktrace diff --git a/.github/workflows/gradle_release.yml b/.github/workflows/gradle_release.yml new file mode 100644 index 0000000000..b08c3fe08e --- /dev/null +++ b/.github/workflows/gradle_release.yml @@ -0,0 +1,70 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Release + +on: + release: + types: [ released, prereleased ] + branches: [ '3.x' ] + tags: + - 'v3.*.*' + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: write + env: + CI_BUILD_NUMBER: ${{ github.run_number }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Set up JDK 11 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'zulu' + java-version: '11' + - name: Cache Gradle packages + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }} + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Grant execute permission for push + run: chmod +x push_javadoc.sh + - name: Extract version tag + run: echo "BUILD_TAG=${GITHUB_REF:10}" >> $GITHUB_ENV + - name: Build RxJava + run: ./gradlew build --stacktrace --no-daemon + - name: Upload to Codecov + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 +# - name: Upload release +# run: ./gradlew -PreleaseMode=full publish --no-daemon --no-parallel --stacktrace +# env: +# # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions +# # ------------------------------------------------------------------------------ +# ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USER }} +# ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} +# ORG_GRADLE_PROJECT_SIGNING_PRIVATE_KEY: ${{ secrets.SIGNING_PRIVATE_KEY }} +# ORG_GRADLE_PROJECT_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + - name: Publish release + run: ./gradlew -PreleaseMode=full publishAndReleaseToMavenCentral --no-configuration-cache --no-daemon --no-parallel --stacktrace + env: + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USER }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_SIGNING_PRIVATE_KEY: ${{ secrets.SIGNING_PRIVATE_KEY }} + ORG_GRADLE_PROJECT_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + - name: Push Javadoc + run: ./push_javadoc.sh + env: + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + JAVADOCS_TOKEN: ${{ secrets.JAVADOCS_TOKEN }} diff --git a/.github/workflows/gradle_snapshot.yml b/.github/workflows/gradle_snapshot.yml new file mode 100644 index 0000000000..5dca06b523 --- /dev/null +++ b/.github/workflows/gradle_snapshot.yml @@ -0,0 +1,56 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Snapshot + +on: + push: + branches: [ '3.x' ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + if: github.repository == 'ReactiveX/RxJava' + permissions: + contents: write + env: + # ------------------------------------------------------------------------------ + CI_BUILD_NUMBER: ${{ github.run_number }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Set up JDK 11 + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'zulu' + java-version: '11' + - name: Cache Gradle packages + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle-${{ secrets.CACHE_VERSION }} + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Grant execute permission for push + run: chmod +x push_javadoc.sh + - name: Build RxJava + run: ./gradlew build --stacktrace --no-daemon + - name: Upload Snapshot + run: ./gradlew -PreleaseMode=branch publishAllPublicationsToMavenCentralRepository --no-daemon --no-parallel --stacktrace + env: + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USER }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} + - name: Upload to Codecov + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + - name: Push Javadoc + run: ./push_javadoc.sh + # Define secrets at https://github.com/ReactiveX/RxJava/settings/secrets/actions + # ------------------------------------------------------------------------------ + env: + JAVADOCS_TOKEN: ${{ secrets.JAVADOCS_TOKEN }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000000..1ebd21eb28 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,59 @@ +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '43 12 * * 4' + push: + branches: [ "3.x" ] + +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + + steps: + - name: "Checkout code" + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if + # you want to enable the Branch-Protection check on a *public* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-fine-grained-pat-optional. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.29.5 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index fb5b67685f..b60171cf2d 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,14 @@ bin/ # Scala build *.cache /.nb-gradle/private/ + +# PMD files +.pmd +.ruleset +test-output/ + +# Checkstyle local config +.checkstyle + +# Some editor's config +.editorconfig diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000000..954870bba6 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,139 @@ +## Learn more about this file at 'https://www.gitpod.io/docs/references/gitpod-yml' +## +## This '.gitpod.yml' file when placed at the root of a project instructs +## Gitpod how to prepare & build the project, start development environments +## and configure continuous prebuilds. Prebuilds when enabled builds a project +## like a CI server so you can start coding right away - no more waiting for +## dependencies to download and builds to finish when reviewing pull-requests +## or hacking on something new. +## +## With Gitpod you can develop software from any device (even iPads) via +## desktop or browser based versions of VS Code or any JetBrains IDE and +## customise it to your individual needs - from themes to extensions, you +## have full control. +## +## The easiest way to try out Gitpod is install the browser extenion: +## 'https://www.gitpod.io/docs/browser-extension' or by prefixing +## 'https://gitpod.io#' to the source control URL of any project. +## +## For example: 'https://gitpod.io#https://github.com/gitpod-io/gitpod' + + +## The 'image' section defines which Docker image Gitpod should use. +## By default, Gitpod uses a standard Docker Image called 'workspace-full' +## which can be found at 'https://github.com/gitpod-io/workspace-images' +## +## Workspaces started based on this default image come pre-installed with +## Docker, Go, Java, Node.js, C/C++, Python, Ruby, Rust, PHP as well as +## tools such as Homebrew, Tailscale, Nginx and several more. +## +## If this image does not include the tools needed for your project then +## a public Docker image or your own Docker file can be configured. +## +## Learn more about images at 'https://www.gitpod.io/docs/config-docker' + +#image: node:buster # use 'https://hub.docker.com/_/node' +# +#image: # leave image undefined if using a Dockerfile +# file: .gitpod.Dockerfile # relative path to the Dockerfile from the +# # root of the project + +## The 'tasks' section defines how Gitpod prepares and builds this project +## or how Gitpod can start development servers. With Gitpod, there are three +## types of tasks: +## +## - before: Use this for tasks that need to run before init and before command. +## - init: Use this to configure prebuilds of heavy-lifting tasks such as +## downloading dependencies or compiling source code. +## - command: Use this to start your database or application when the workspace starts. +## +## Learn more about these tasks at 'https://www.gitpod.io/docs/config-start-tasks' + +#tasks: +# - before: | +# # commands to execute... +# +# - init: | +# # sudo apt-get install python3 # can be used to install operating system +# # dependencies but these are not kept after the +# # prebuild completes thus Gitpod recommends moving +# # operating system dependency installation steps +# # to a custom Dockerfile to make prebuilds faster +# # and to keep your codebase DRY. +# # 'https://www.gitpod.io/docs/config-docker' +# +# # pip install -r requirements.txt # install codebase dependencies +# # cmake # precompile codebase +# +# - name: Web Server +# openMode: split-left +# env: +# WEBSERVER_PORT: 8080 +# command: | +# python3 -m http.server $WEBSERVER_PORT +# +# - name: Web Browser +# openMode: split-right +# env: +# WEBSERVER_PORT: 8080 +# command: | +# gp await-port $WEBSERVER_PORT +# lynx `gp url` + +tasks: + - command: ./gradlew build + +## The 'ports' section defines various ports your may listen on are +## configured in Gitpod on an authenticated URL. By default, all ports +## are in private visibility state. +## +## Learn more about ports at 'https://www.gitpod.io/docs/config-ports' + +#ports: +# - port: 8080 # alternatively configure entire ranges via '8080-8090' +# visibility: private # either 'public' or 'private' (default) +# onOpen: open-browser # either 'open-browser', 'open-preview' or 'ignore' + + +## The 'vscode' section defines a list of Visual Studio Code extensions from +## the OpenVSX.org registry to be installed upon workspace startup. OpenVSX +## is an open alternative to the proprietary Visual Studio Code Marketplace +## and extensions can be added by sending a pull-request with the extension +## identifier to https://github.com/open-vsx/publish-extensions +## +## The identifier of an extension is always ${publisher}.${name}. +## +## For example: 'vscodevim.vim' +## +## Learn more at 'https://www.gitpod.io/docs/ides-and-editors/vscode' + +#vscode: +# extensions: +# - vscodevim.vim +# - esbenp.prettier-vscode@9.5.0 +# - https://example.com/abc/releases/extension-0.26.0.vsix + + +## The 'github' section defines configuration of continuous prebuilds +## for GitHub repositories when the GitHub application +## 'https://github.com/apps/gitpod-io' is installed in GitHub and granted +## permissions to access the repository. +## +## Learn more at 'https://www.gitpod.io/docs/prebuilds' + +github: + prebuilds: + # enable for the default branch + master: true + # enable for all branches in this repo + branches: true + # enable for pull requests coming from this repo + pullRequests: true + # enable for pull requests coming from forks + pullRequestsFromForks: true + # add a check to pull requests + addCheck: true + # add a "Review in Gitpod" button as a comment to pull requests + addComment: false + # add a "Review in Gitpod" button to the pull request's description + addBadge: true diff --git a/CHANGES.md b/CHANGES.md deleted file mode 100644 index b6f2d5d6c8..0000000000 --- a/CHANGES.md +++ /dev/null @@ -1,1714 +0,0 @@ -# RxJava Releases # - -### Version 0.19.6 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.19.6%22)) ### - -Inclusion of 'rxjava-contrib:rxjava-scalaz' in release. - -### Version 0.19.5 ### - -Upload to Maven Central was corrupted so release is skipped. - -### Version 0.19.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.19.4%22)) ### - -* [Pull 1401] (https://github.com/Netflix/RxJava/pull/1401) OnError while emitting onNext value: object.toString -* [Pull 1409] (https://github.com/Netflix/RxJava/pull/1409) Avoiding OperatorObserveOn from calling subscriber.onNext(..) after unsubscribe -* [Pull 1406] (https://github.com/Netflix/RxJava/pull/1406) Kotlin M8 -* [Pull 1400] (https://github.com/Netflix/RxJava/pull/1400) Internal Data Structures -* [Pull 1399] (https://github.com/Netflix/RxJava/pull/1399) Update Perf Tests -* [Pull 1396] (https://github.com/Netflix/RxJava/pull/1396) RxScala: Fix the compiler warnings -* [Pull 1397] (https://github.com/Netflix/RxJava/pull/1397) Adding the hooks unsafeSubscribe - -### Version 0.19.3 ### - -Upload to Maven Central was corrupted so release is skipped. - -### Version 0.19.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.19.2%22)) ### - -* [Pull 1388] (https://github.com/Netflix/RxJava/pull/1388) CompositeException stops mutating nested Exceptions -* [Pull 1387] (https://github.com/Netflix/RxJava/pull/1387) Upgrade to JMH 0.9 -* [Pull 1297] (https://github.com/Netflix/RxJava/pull/1297) [RxScala] rxjava-scalaz: providing some type class instances -* [Pull 1332] (https://github.com/Netflix/RxJava/pull/1332) IOSSchedulers for RoboVM -* [Pull 1380] (https://github.com/Netflix/RxJava/pull/1380) Variety of Fixes -* [Pull 1379] (https://github.com/Netflix/RxJava/pull/1379) Parallel Operator Rewrite -* [Pull 1378] (https://github.com/Netflix/RxJava/pull/1378) BugFix: Pivot Concurrency -* [Pull 1376] (https://github.com/Netflix/RxJava/pull/1376) Revision of JMH Tests -* [Pull 1375] (https://github.com/Netflix/RxJava/pull/1375) RxScala: Add idiomatic toXXX methods -* [Pull 1367] (https://github.com/Netflix/RxJava/pull/1367) Fix the bug that 'flatMap' swallows OnErrorNotImplementedException -* [Pull 1374] (https://github.com/Netflix/RxJava/pull/1374) Fix head/tail false sharing issues -* [Pull 1369] (https://github.com/Netflix/RxJava/pull/1369) DebugHook got miswired before -* [Pull 1361] (https://github.com/Netflix/RxJava/pull/1361) Fix a race condition if queued actions have been handled already -* [Pull 1336] (https://github.com/Netflix/RxJava/pull/1336) RxScala: Add the rest missing methods to BlockingObservable -* [Pull 1362] (https://github.com/Netflix/RxJava/pull/1362) RxScala: Fix #1340 and #1343 -* [Pull 1359] (https://github.com/Netflix/RxJava/pull/1359) Fixed padding of the integer and node classes - -### Version 0.19.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.19.1%22)) ### - -* [Pull 1357] (https://github.com/Netflix/RxJava/pull/1357) MergeWith, ConcatWith, AmbWith -* [Pull 1345] (https://github.com/Netflix/RxJava/pull/1345) RxScala: Simplify doOnCompleted/Terminate, finallyDo callback usage -* [Pull 1337] (https://github.com/Netflix/RxJava/pull/1337) Make Future receive NoSuchElementException when the BlockingObservable is empty -* [Pull 1335] (https://github.com/Netflix/RxJava/pull/1335) RxAndroid: Bump build tools to 19.1 and android plugin to 0.11 -* [Pull 1327] (https://github.com/Netflix/RxJava/pull/1327) Join patterns extension for 4..9 and N arity joins. -* [Pull 1321] (https://github.com/Netflix/RxJava/pull/1321) RxAndroid: Ensuring Runnables posted with delay to a Handler are removed when unsubcribed -* [Pull 1347] (https://github.com/Netflix/RxJava/pull/1347) Allow use of the returned subscription to cancel periodic scheduling -* [Pull 1355] (https://github.com/Netflix/RxJava/pull/1355) Don't add the subscriber to the manager if it unsubscribed during the onStart call -* [Pull 1350] (https://github.com/Netflix/RxJava/pull/1350) Baseline Performance Tests -* [Pull 1316] (https://github.com/Netflix/RxJava/pull/1316) RxScala: Add the rest operators -* [Pull 1324] (https://github.com/Netflix/RxJava/pull/1324) TrampolineScheduler & Unsubscribe -* [Pull 1311] (https://github.com/Netflix/RxJava/pull/1311) Tiny integration test change - - -### Version 0.19.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.19.0%22)) ### - -#### Performance and Object Allocation - -Fairly significant object allocation improvements are included in this release which reduce GC pressure and improve performance. - -Two pull requests (amongst several) with details are: - -- https://github.com/Netflix/RxJava/pull/1281 Reduce Subscription Object Allocation -- https://github.com/Netflix/RxJava/pull/1246 Moved to atomic field updaters - -With the following simple test code relative performance has increased as shown below: - -```java -Observable o = Observable.just(1); -o.map(i -> { - return String.valueOf(i); -}).map(i -> { - return Integer.parseInt(i); -}).subscribe(observer); -``` - - -###### Rx 0.19 - -``` -Run: 10 - 10,692,099 ops/sec -Run: 11 - 10,617,627 ops/sec -Run: 12 - 10,938,405 ops/sec -Run: 13 - 10,917,388 ops/sec -Run: 14 - 10,783,298 ops/sec -``` - -###### Rx 0.18.4 - -``` -Run: 11 - 8,493,506 ops/sec -Run: 12 - 8,403,361 ops/sec -Run: 13 - 8,400,537 ops/sec -Run: 14 - 8,163,998 ops/sec -``` - -###### Rx 0.17.6 - -``` -Run: 10 - 4,930,966 ops/sec -Run: 11 - 6,119,951 ops/sec -Run: 12 - 7,062,146 ops/sec -Run: 13 - 6,514,657 ops/sec -Run: 14 - 6,369,426 ops/sec -``` - -###### Rx 0.16.1 - -``` -Run: 10 - 2,879,355 ops/sec -Run: 11 - 3,236,245 ops/sec -Run: 12 - 4,468,275 ops/sec -Run: 13 - 3,237,293 ops/sec -Run: 14 - 4,683,840 ops/sec -``` - -Note that these numbers are relative as they depend on the JVM and hardware. - - -#### Scala Changes - -Many missing operators have been added to the RxScala APIs along with fixes and other maturation. - - -#### toBlockingObservable() -> toBlocking() - -The `toBlockingObservable()` method has been deprecated in favor of `toBlocking()` for brevity and fit better with possible future additions such as `toParallel()` without always needing the `Observable` suffix. - - -#### forEach - -`forEach` as added as an alias for `subscribe` to match the Java 8 naming convention. - -This means code can now be written as: - -```java -Observable.from(1, 2, 3).limit(2).forEach(System.out::println); -``` - -which is an alias of this: - -```java -Observable.from(1, 2, 3).take(2).subscribe(System.out::println); -``` - -Since `forEach` exists on `BlockingObservable` as well, moving from non-blocking to blocking looks like this: - -```java -// non-blocking -Observable.from(1, 2, 3).limit(2).forEach(System.out::println); -// blocking -Observable.from(1, 2, 3).limit(2).toBlocking().forEach(System.out::println); -``` - - -#### Schedulers - -Thread caching is restored to `Schedulers.io()` after being lost in v0.18. - -A replacement for `ExecutorScheduler` (removed in 0.18) is accessible via `Schedulers.from(Executor e)` that wraps an `Executor` and complies with the Rx contract. - - -#### ReplaySubject - -All "replay" functionality now exists directly on the `ReplaySubject` rather than in an internal type. This means there are now several different `create` methods with the various overloads of size and time. - - -#### Changelist - -* [Pull 1165](https://github.com/Netflix/RxJava/pull/1165) RxScala: Add dropUntil, contains, repeat, doOnTerminate, startWith, publish variants -* [Pull 1183](https://github.com/Netflix/RxJava/pull/1183) NotificationLite.accept performance improvements -* [Pull 1177](https://github.com/Netflix/RxJava/pull/1177) GroupByUntil to use BufferUntilSubscriber -* [Pull 1182](https://github.com/Netflix/RxJava/pull/1182) Add facilities for creating Observables from JavaFX events and ObservableValues -* [Pull 1188](https://github.com/Netflix/RxJava/pull/1188) RxScala Schedulers changes -* [Pull 1175](https://github.com/Netflix/RxJava/pull/1175) Fixed synchronous ConnectableObservable.connect problem -* [Pull 1172](https://github.com/Netflix/RxJava/pull/1172) ObserveOn: Change to batch dequeue -* [Pull 1191](https://github.com/Netflix/RxJava/pull/1191) Fix attempt for OperatorPivotTest -* [Pull 1195](https://github.com/Netflix/RxJava/pull/1195) SwingScheduler: allow negative schedule -* [Pull 1178](https://github.com/Netflix/RxJava/pull/1178) Fix RxScala bug -* [Pull 1210](https://github.com/Netflix/RxJava/pull/1210) Add more operators to RxScala -* [Pull 1216](https://github.com/Netflix/RxJava/pull/1216) RxScala: Exposing PublishSubject -* [Pull 1208](https://github.com/Netflix/RxJava/pull/1208) OperatorToObservableList: use LinkedList to buffer the sequence’s items -* [Pull 1185](https://github.com/Netflix/RxJava/pull/1185) Behavior subject time gap fix 2 -* [Pull 1226](https://github.com/Netflix/RxJava/pull/1226) Fix bug in `zipWithIndex` and set `zip(that, selector)` public in RxScala -* [Pull 1224](https://github.com/Netflix/RxJava/pull/1224) Implement shorter toBlocking as shorter alias for toBlockingObservable. -* [Pull 1223](https://github.com/Netflix/RxJava/pull/1223) ReplaySubject enhancement with time and/or size bounds -* [Pull 1160](https://github.com/Netflix/RxJava/pull/1160) Add `replay` and `multicast` variants to RxScala -* [Pull 1229](https://github.com/Netflix/RxJava/pull/1229) Remove Ambiguous Subscribe Overloads with Scheduler -* [Pull 1232](https://github.com/Netflix/RxJava/pull/1232) Adopt Limit and ForEach Java 8 Naming Conventions -* [Pull 1233](https://github.com/Netflix/RxJava/pull/1233) Deprecate toBlockingObservable in favor of toBlocking -* [Pull 1237](https://github.com/Netflix/RxJava/pull/1237) SafeSubscriber memory reduction -* [Pull 1236](https://github.com/Netflix/RxJava/pull/1236) CompositeSubscription with atomic field updater -* [Pull 1243](https://github.com/Netflix/RxJava/pull/1243) Remove Subscription Wrapper from Observable.subscribe -* [Pull 1244](https://github.com/Netflix/RxJava/pull/1244) Observable.from(T) using Observable.just(T) -* [Pull 1239](https://github.com/Netflix/RxJava/pull/1239) RxScala: Update docs for "apply" and add an example -* [Pull 1248](https://github.com/Netflix/RxJava/pull/1248) Fixed testConcurrentOnNextFailsValidation -* [Pull 1246](https://github.com/Netflix/RxJava/pull/1246) Moved to atomic field updaters. -* [Pull 1254](https://github.com/Netflix/RxJava/pull/1254) ZipIterable unsubscription fix -* [Pull 1247](https://github.com/Netflix/RxJava/pull/1247) Add zip(iterable, selector) to RxScala -* [Pull 1260](https://github.com/Netflix/RxJava/pull/1260) Fix the bug that BlockingObservable.singleOrDefault doesn't call unsubscribe -* [Pull 1269](https://github.com/Netflix/RxJava/pull/1269) Fix the bug that int overflow can bypass the range check -* [Pull 1272](https://github.com/Netflix/RxJava/pull/1272) ExecutorScheduler to wrap an Executor -* [Pull 1264](https://github.com/Netflix/RxJava/pull/1264) ObserveOn scheduled unsubscription -* [Pull 1271](https://github.com/Netflix/RxJava/pull/1271) Operator Retry with predicate -* [Pull 1265](https://github.com/Netflix/RxJava/pull/1265) Add more operators to RxScala -* [Pull 1281](https://github.com/Netflix/RxJava/pull/1281) Reduce Subscription Object Allocation -* [Pull 1284](https://github.com/Netflix/RxJava/pull/1284) Lock-free, MPSC-queue -* [Pull 1288](https://github.com/Netflix/RxJava/pull/1288) Ensure StringObservable.from() does not perform unnecessary read -* [Pull 1286](https://github.com/Netflix/RxJava/pull/1286) Rename some Operator* classes to OnSubscribe* -* [Pull 1276](https://github.com/Netflix/RxJava/pull/1276) CachedThreadScheduler -* [Pull 1287](https://github.com/Netflix/RxJava/pull/1287) ReplaySubject remove replayState CHM and related SubjectObserver changes -* [Pull 1289](https://github.com/Netflix/RxJava/pull/1289) Schedulers.from(Executor) -* [Pull 1290](https://github.com/Netflix/RxJava/pull/1290) Upgrade to JMH 0.7.3 -* [Pull 1293](https://github.com/Netflix/RxJava/pull/1293) Fix and Update JMH Perf Tests -* [Pull 1291](https://github.com/Netflix/RxJava/pull/1291) Check unsubscribe within observable from future -* [Pull 1294](https://github.com/Netflix/RxJava/pull/1294) rx.operators -> rx.internal.operators -* [Pull 1295](https://github.com/Netflix/RxJava/pull/1295) Change `void accept` to `boolean accept` -* [Pull 1296](https://github.com/Netflix/RxJava/pull/1296) Move re-used internal Scheduler classes to their own package -* [Pull 1298](https://github.com/Netflix/RxJava/pull/1298) Remove Bad Perf Test -* [Pull 1301](https://github.com/Netflix/RxJava/pull/1301) RxScala: Add convenience method for adding unsubscription callback -* [Pull 1304](https://github.com/Netflix/RxJava/pull/1304) Add flatMap and concatMap to RxScala -* [Pull 1306](https://github.com/Netflix/RxJava/pull/1306) Hooked RxJavaPlugins errorHandler up within all operators that swallow onErrors -* [Pull 1309](https://github.com/Netflix/RxJava/pull/1309) Hide ChainedSubscription/SubscriptionList from Public API - -### Version 0.18.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.18.4%22)) ### - -This is a fix for `CompositeSubscription` object allocation problems. Details can be found in issue [#1204](https://github.com/Netflix/RxJava/issues/1204). - -* [Pull 1283](https://github.com/Netflix/RxJava/pull/1283) Subscription object allocation fix - -### Version 0.18.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.18.3%22)) ### - -* [Pull 1161] (https://github.com/Netflix/RxJava/pull/1161) Removed use of deprecated API from tests & operators -* [Pull 1162] (https://github.com/Netflix/RxJava/pull/1162) fix to remove drift from schedulePeriodic -* [Pull 1159] (https://github.com/Netflix/RxJava/pull/1159) Rxscala improvement -* [Pull 1164] (https://github.com/Netflix/RxJava/pull/1164) JMH Perf Tests for Schedulers.computation -* [Pull 1158] (https://github.com/Netflix/RxJava/pull/1158) Scheduler correctness improvements. - -### Version 0.18.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.18.2%22)) ### - - -* [Pull 1150] (https://github.com/Netflix/RxJava/pull/1150) Fix ReplaySubject Terminal State Race Condition -* [Pull 1144] (https://github.com/Netflix/RxJava/pull/1144) Operator Delay rebase & fixes -* [Pull 1142] (https://github.com/Netflix/RxJava/pull/1142) Update 'contains' signature to 'contains(Object)' -* [Pull 1134] (https://github.com/Netflix/RxJava/pull/1134) OperatorTakeLast -* [Pull 1135] (https://github.com/Netflix/RxJava/pull/1135) OperatorTakeUntil -* [Pull 1137] (https://github.com/Netflix/RxJava/pull/1137) Random fixes to operators multicast, sample, refCount -* [Pull 1138] (https://github.com/Netflix/RxJava/pull/1138) Operator Window and other changes -* [Pull 1131] (https://github.com/Netflix/RxJava/pull/1131) Operator TakeTimed -* [Pull 1130] (https://github.com/Netflix/RxJava/pull/1130) Operator Switch -* [Pull 1129] (https://github.com/Netflix/RxJava/pull/1129) Conditional statements contribution to Operator -* [Pull 1128] (https://github.com/Netflix/RxJava/pull/1128) Fix for SerializedObserverTest -* [Pull 1126] (https://github.com/Netflix/RxJava/pull/1126) Operator When -* [Pull 1125] (https://github.com/Netflix/RxJava/pull/1125) Operator contrib math -* [Pull 1124] (https://github.com/Netflix/RxJava/pull/1124) Add lift to rxscala -* [Pull 1122] (https://github.com/Netflix/RxJava/pull/1122) OperatorSkipUntil -* [Pull 1121] (https://github.com/Netflix/RxJava/pull/1121) OperatorSkipTimed -* [Pull 1120] (https://github.com/Netflix/RxJava/pull/1120) OperatorSequenceEqual -* [Pull 1119] (https://github.com/Netflix/RxJava/pull/1119) OperatorRefCount -* [Pull 1118] (https://github.com/Netflix/RxJava/pull/1118) Operator ParallelMerge -* [Pull 1117] (https://github.com/Netflix/RxJava/pull/1117) Operator OnExceptionResumeNextViaObservable -* [Pull 1115] (https://github.com/Netflix/RxJava/pull/1115) OperatorTakeWhile -* [Pull 1112] (https://github.com/Netflix/RxJava/pull/1112) OperatorThrottleFirst -* [Pull 1111] (https://github.com/Netflix/RxJava/pull/1111) OperatorTimeInterval -* [Pull 1110] (https://github.com/Netflix/RxJava/pull/1110) OperatorOnErrorReturn -* [Pull 1109] (https://github.com/Netflix/RxJava/pull/1109) OperatorOnErrorResumeNextViaObservable -* [Pull 1108] (https://github.com/Netflix/RxJava/pull/1108) OperatorMulticastAndReplay -* [Pull 1107] (https://github.com/Netflix/RxJava/pull/1107) Fix ReplaySubject's double termination problem. -* [Pull 1106] (https://github.com/Netflix/RxJava/pull/1106) OperatorMergeMaxConcurrent -* [Pull 1104] (https://github.com/Netflix/RxJava/pull/1104) Operator merge delay error -* [Pull 1103] (https://github.com/Netflix/RxJava/pull/1103) OperatorJoin -* [Pull 1101] (https://github.com/Netflix/RxJava/pull/1101) Operator async -* [Pull 1100] (https://github.com/Netflix/RxJava/pull/1100) OperatorUsing -* [Pull 1099] (https://github.com/Netflix/RxJava/pull/1099) OperatorToMap -* [Pull 1098] (https://github.com/Netflix/RxJava/pull/1098) OperatorTimerAndSample -* [Pull 1097] (https://github.com/Netflix/RxJava/pull/1097) OperatorToMultimap -* [Pull 1096] (https://github.com/Netflix/RxJava/pull/1096) OperatorGroupJoin -* [Pull 1095] (https://github.com/Netflix/RxJava/pull/1095) OperatorGroupByUntil -* [Pull 1094] (https://github.com/Netflix/RxJava/pull/1094) Operator debounce - - -### Version 0.18.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.18.1%22)) ### - -* [Pull 1065] (https://github.com/Netflix/RxJava/pull/1065) Optimize OperatorSkipLastTimed -* [Pull 1073] (https://github.com/Netflix/RxJava/pull/1073) OperatorBuffer -* [Pull 1074] (https://github.com/Netflix/RxJava/pull/1074) OperatorConcat -* [Pull 1088] (https://github.com/Netflix/RxJava/pull/1088) OperatorToObservableFuture -* [Pull 1087] (https://github.com/Netflix/RxJava/pull/1087) OperatorMergeMap -* [Pull 1086] (https://github.com/Netflix/RxJava/pull/1086) OperatorFinallyDo -* [Pull 1085] (https://github.com/Netflix/RxJava/pull/1085) OperatorDistinctUntilChanged -* [Pull 1084] (https://github.com/Netflix/RxJava/pull/1084) OperatorDistinct -* [Pull 1083] (https://github.com/Netflix/RxJava/pull/1083) OperatorDematerialize -* [Pull 1081] (https://github.com/Netflix/RxJava/pull/1081) OperatorDefer -* [Pull 1080] (https://github.com/Netflix/RxJava/pull/1080) OperatorDefaultIfEmpty -* [Pull 1079] (https://github.com/Netflix/RxJava/pull/1079) OperatorCombineLatest -* [Pull 1074] (https://github.com/Netflix/RxJava/pull/1074) OperatorConcat -* [Pull 1073] (https://github.com/Netflix/RxJava/pull/1073) OperatorBuffer -* [Pull 1091] (https://github.com/Netflix/RxJava/pull/1091) Handle Thrown Errors with UnsafeSubscribe -* [Pull 1092] (https://github.com/Netflix/RxJava/pull/1092) Restore ObservableExecutionHook.onCreate - -### Version 0.18.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.18.0%22)) ### - -This release takes us a step closer to [1.0](https://github.com/Netflix/RxJava/issues/1001) by completing some of the remaining work on the roadmap. - -##### Scheduler - -The first is simplifying the [Scheduler API](https://github.com/Netflix/RxJava/issues/997). - -The Scheduler API is now simplified to this: - -```java -class Scheduler { - public abstract Worker createWorker(); - public int parallelism(); - public long now(); - - public abstract static class Worker implements Subscription { - public abstract Subscription schedule(Action0 action, long delayTime, TimeUnit unit); - public abstract Subscription schedule(Action0 action); - public Subscription schedulePeriodically(Action0 action, long initialDelay, long period, TimeUnit unit); - public long now(); - } -} -``` - -This is a breaking change if you have a custom `Scheduler` implementation or use a `Scheduler` directly. If you only ever pass in a `Scheduler` via the `Schedulers` factory methods, this change does not affect you. - -Additionally, the `ExecutionScheduler` was removed because a general threadpool does not meet the requirements of sequential execution for an `Observable`. It was replaced with `rx.schedulers.EventLoopScheduler` which is the new default for `Schedulers.computation()`. It is a pool of event loops. - -##### rx.joins - -The `rx.joins` package and associated `when`, `and` and `then` operators were moved out of rxjava-core into a new module rxjava-joins. This is done as the rx.joins API was not yet matured and is not going to happen before 1.0. It was determined low priority and not worth blocking a 1.0 release. If the API matures inside the separate module to the point where it makes sense to bring it back into the core it can be done in the 1.x series. - -##### Deprecation Cleanup - -This releases removes many of the classes and methods that have been deprecated in previous releases. Most of the removed functionality was migrated in previous releases to contrib modules such as rxjava-math, rxjava-async and rxjava-computation-expressions. - -A handful of deprecated items still remain but can not yet be removed until all internal operators are finished migrating to using the `lift`/`Subscriber` design changes done in 0.17.0. - - -The full list of changes in 0.18.0: - -* [Pull 1047] (https://github.com/Netflix/RxJava/pull/1047) Scheduler Simplification -* [Pull 1072] (https://github.com/Netflix/RxJava/pull/1072) Scheduler.Inner -> Scheduler.Worker -* [Pull 1053] (https://github.com/Netflix/RxJava/pull/1053) Deprecation Cleanup -* [Pull 1052] (https://github.com/Netflix/RxJava/pull/1052) Scheduler Cleanup -* [Pull 1048] (https://github.com/Netflix/RxJava/pull/1048) Remove ExecutorScheduler - New ComputationScheduler -* [Pull 1049] (https://github.com/Netflix/RxJava/pull/1049) Move rx.joins to rxjava-joins module -* [Pull 1068] (https://github.com/Netflix/RxJava/pull/1068) add synchronous test of resubscribe after error -* [Pull 1066] (https://github.com/Netflix/RxJava/pull/1066) CompositeSubscription fix -* [Pull 1071] (https://github.com/Netflix/RxJava/pull/1071) Manual Merge of AsObservable -* [Pull 1063] (https://github.com/Netflix/RxJava/pull/1063) Fix bugs in equals and hashCode of Timestamped -* [Pull 1070] (https://github.com/Netflix/RxJava/pull/1070) OperationAny -> OperatorAny -* [Pull 1069] (https://github.com/Netflix/RxJava/pull/1069) OperationAll -> OperatorAll -* [Pull 1058] (https://github.com/Netflix/RxJava/pull/1058) Typo in javadoc -* [Pull 1056] (https://github.com/Netflix/RxJava/pull/1056) Add drop(skip) and dropRight(skipLast) to rxscala -* [Pull 1057] (https://github.com/Netflix/RxJava/pull/1057) Fix: Retry in Scala adaptor is ambiguous -* [Pull 1055] (https://github.com/Netflix/RxJava/pull/1055) Fix: Missing Quasar instrumentation on Observable$2.call -* [Pull 1050] (https://github.com/Netflix/RxJava/pull/1050) Reimplement the 'SkipLast' operator -* [Pull 967] (https://github.com/Netflix/RxJava/pull/967) Reimplement the 'single' operator - - - -### Version 0.17.6 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.17.6%22)) ### - -* [Pull 1031] (https://github.com/Netflix/RxJava/pull/1031) Fix NPE in SubjectSubscriptionManager -* [Pull 1030] (https://github.com/Netflix/RxJava/pull/1030) Benchmarking: Add JMH benchmark for ReplaySubject -* [Pull 1033] (https://github.com/Netflix/RxJava/pull/1033) isolate subscriber used for retries, cleanup tests -* [Pull 1021] (https://github.com/Netflix/RxJava/pull/1021) OperatorWeakBinding to not use WeakReferences anymore -* [Pull 1005] (https://github.com/Netflix/RxJava/pull/1005) add toMap from Java Observable -* [Pull 1040] (https://github.com/Netflix/RxJava/pull/1040) Fixed deadlock in Subjects + OperatorCache -* [Pull 1042] (https://github.com/Netflix/RxJava/pull/1042) Kotlin M7 and full compatibility with 0.17.0 -* [Pull 1035] (https://github.com/Netflix/RxJava/pull/1035) Scala cleanup -* [Pull 1009] (https://github.com/Netflix/RxJava/pull/1009) Android - Adding a new RetainedFragment example -* [Pull 1020] (https://github.com/Netflix/RxJava/pull/1020) Upgrade Gradle wrapper for Android samples to Gradle 1.11 -* [Pull 1038] (https://github.com/Netflix/RxJava/pull/1038) rxjava-android: parameterize OperatorViewClick by concrete view type - -### Version 0.17.5 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.17.5%22)) ### - -* [Pull 1010] (https://github.com/Netflix/RxJava/pull/1010) Observable.unsafeSubscribe -* [Pull 1015] (https://github.com/Netflix/RxJava/pull/1015) Remove Redundant protectivelyWrap Method -* [Pull 1019] (https://github.com/Netflix/RxJava/pull/1019) Fix: retry() never unsubscribes from source until operator completes - -### Version 0.17.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.17.4%22)) ### - -* [Pull 990] (https://github.com/Netflix/RxJava/pull/990) Quasar Lightweight Threads/Fibers Contrib Module -* [Pull 1012] (https://github.com/Netflix/RxJava/pull/1012) SerializedObserver: Removed window between the two synchronized blocks - - -### Version 0.17.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.17.3%22)) ### - -* [Pull 991] (https://github.com/Netflix/RxJava/pull/991) JMH Benchmark Build Config -* [Pull 993] (https://github.com/Netflix/RxJava/pull/993) JMH Perf Tests -* [Pull 995] (https://github.com/Netflix/RxJava/pull/995) Support Custom JMH Args -* [Pull 996] (https://github.com/Netflix/RxJava/pull/996) JMH Perfshadowjar -* [Pull 1003] (https://github.com/Netflix/RxJava/pull/1003) Func0 can transparently implement java.util.concurrent.Callable -* [Pull 999] (https://github.com/Netflix/RxJava/pull/999) New Implementation of SerializedObserver - -### Version 0.17.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.17.2%22)) ### - -* [Pull 963](https://github.com/Netflix/RxJava/pull/963) A more robust JMH benchmarking set-up -* [Pull 964](https://github.com/Netflix/RxJava/pull/964) SubjectSubscriptionManager fix. -* [Pull 970](https://github.com/Netflix/RxJava/pull/970) Notifications for the allocation averse. -* [Pull 973](https://github.com/Netflix/RxJava/pull/973) Merge - Handle Bad Observables -* [Pull 974](https://github.com/Netflix/RxJava/pull/974) TestSubject, TestObserver and TestScheduler Improvements -* [Pull 975](https://github.com/Netflix/RxJava/pull/975) GroupBy & Time Gap Fixes -* [Pull 976](https://github.com/Netflix/RxJava/pull/976) parallel-merge unit test assertions -* [Pull 977](https://github.com/Netflix/RxJava/pull/977) Dematerialize - handle non-materialized terminal events -* [Pull 982](https://github.com/Netflix/RxJava/pull/982) Pivot Operator -* [Pull 984](https://github.com/Netflix/RxJava/pull/984) Tests and Javadoc for Pivot -* [Pull 966](https://github.com/Netflix/RxJava/pull/966) Reimplement the ElementAt operator and add it to rxjava-scala -* [Pull 965](https://github.com/Netflix/RxJava/pull/965) BugFix: Chain Subscription in TimeoutSubscriber and SerializedSubscriber -* [Pull 986](https://github.com/Netflix/RxJava/pull/986) Fix SynchronizedObserver.runConcurrencyTest -* [Pull 987](https://github.com/Netflix/RxJava/pull/987) Fix Non-Deterministic Pivot Test -* [Pull 988](https://github.com/Netflix/RxJava/pull/988) OnErrorFailedException - - -### Version 0.17.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.17.1%22)) ### - -* [Pull 953](https://github.com/Netflix/RxJava/pull/953) Make ObserveOnTest.testNonBlockingOuterWhileBlockingOnNext deterministic -* [Pull 930](https://github.com/Netflix/RxJava/pull/930) Initial commit of the Android samples module -* [Pull 938](https://github.com/Netflix/RxJava/pull/938) OperatorWeakBinding (deprecates OperatorObserveFromAndroidComponent) -* [Pull 952](https://github.com/Netflix/RxJava/pull/952) rxjava-scala improvements and reimplemented the `amb` operator -* [Pull 955](https://github.com/Netflix/RxJava/pull/955) Fixed ReplaySubject leak -* [Pull 956](https://github.com/Netflix/RxJava/pull/956) Fixed byLine test to use line.separator system property instead of \n. -* [Pull 958](https://github.com/Netflix/RxJava/pull/958) OperatorSkipWhile -* [Pull 959](https://github.com/Netflix/RxJava/pull/959) OperationToFuture must throw CancellationException on get() if cancelled -* [Pull 928](https://github.com/Netflix/RxJava/pull/928) Fix deadlock in SubscribeOnBounded -* [Pull 960](https://github.com/Netflix/RxJava/pull/960) Unit test for "Cannot subscribe to a Retry observable once all subscribers unsubscribed" -* [Pull 962](https://github.com/Netflix/RxJava/pull/962) Migrate from SynchronizedObserver to SerializedObserver - - -### Version 0.17.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.17.0%22)) ### - - -Version 0.17.0 contains some significant signature changes that allow us to significantly improve handling of synchronous Observables and simplify Schedulers. Many of the changes have backwards compatible deprecated methods to ease the migration while some are breaking. - -The new signatures related to `Observable` in this release are: - -```java -// A new create method takes `OnSubscribe` instead of `OnSubscribeFunc` -public final static Observable create(OnSubscribe f) - -// The new OnSubscribe type accepts a Subscriber instead of Observer and does not return a Subscription -public static interface OnSubscribe extends Action1> - -// Subscriber is an Observer + Subscription -public abstract class Subscriber implements Observer, Subscription - -// The main `subscribe` behavior receives a Subscriber instead of Observer -public final Subscription subscribe(Subscriber subscriber) - -// Subscribing with an Observer however is still appropriate -// and the Observer is automatically converted into a Subscriber -public final Subscription subscribe(Observer observer) - -// A new 'lift' function allows composing Operator implementations together -public Observable lift(final Operator lift) - -// The `Operator` used with `lift` -public interface Operator extends Func1, Subscriber> - -``` - -Also changed is the `Scheduler` interface which is much simpler: - -```java -public abstract class Scheduler { - public Subscription schedule(Action1 action); - public Subscription schedule(Action1 action, long delayTime, TimeUnit unit); - public Subscription schedulePeriodically(Action1 action, long initialDelay, long period, TimeUnit unit); - public final Subscription scheduleRecursive(final Action1 action) - public long now(); - public int degreeOfParallelism(); - - public static class Inner implements Subscription { - public abstract void schedule(Action1 action, long delayTime, TimeUnit unit); - public abstract void schedule(Action1 action); - public long now(); - } - - public static final class Recurse { - public final void schedule(); - public final void schedule(long delay, TimeUnit unit); - } -} -``` - - -This release applies many lessons learned over the past year and seeks to streamline the API before we hit 1.0. - -As shown in the code above the changes fall into 2 major sections: - -##### 1) Lift/Operator/OnSubscribe/Subscriber - -Changes that allow unsubscribing from synchronous Observables without needing to add concurrency. - -##### 2) Schedulers - -Simplification of the `Scheduler` interface and make clearer the concept of "outer" and "inner" Schedulers for recursion. - - -#### Lift/Operator/OnSubscribe/Subscriber - -New types `Subscriber` and `OnSubscribe` along with the new `lift` function have been added. The reasons and benefits are as follows: - -##### 1) Synchronous Unsubscribe - -RxJava versions up until 0.16.x are unable to unsubscribe from a synchronous Observable such as this: - -```java -Observable oi = Observable.create(new OnSubscribe() { - - @Override - public void call(Observer Observer) { - for (int i = 1; i < 1000000; i++) { - subscriber.onNext(i); - } - subscriber.onCompleted(); - } -}); -``` - -Subscribing to this `Observable` will always emit all 1,000,000 values even if unsubscribed such as via `oi.take(10)`. - -Version 0.17.0 fixes this issue by injecting the `Subscription` into the `OnSubscribe` function to allow code like this: - -```java -Observable oi = Observable.create(new OnSubscribe() { - - @Override - public void call(Subscriber subscriber) { - // we now receive a Subscriber instead of Observer - for (int i = 1; i < 1000000; i++) { - // the OnSubscribe can now check for isUnsubscribed - if (subscriber.isUnsubscribed()) { - return; - } - subscriber.onNext(i); - } - subscriber.onCompleted(); - } - -}); -``` - -Subscribing to this will now correctly only emit 10 `onNext` and unsubscribe: - -```java -// subscribe with an Observer -oi.take(10).subscribe(new Observer() { - - @Override - public void onCompleted() { - - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onNext(Integer t) { - println("Received: " + t); - } - -}) -``` - -Or the new `Subscriber` type can be used and the `Subscriber` itself can `unsubscribe`: - -```java -// or subscribe with a Subscriber which supports unsubscribe -oi.subscribe(new Subscriber() { - - @Override - public void onCompleted() { - - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onNext(Integer t) { - println("Received: " + t); - if(t >= 10) { - // a Subscriber can unsubscribe - this.unsubscribe(); - } - } - -}) -``` - - -##### 2) Custom Operator Chaining - -Because Java doesn't support extension methods, the only approach to applying custom operators without getting them added to `rx.Observable` is using static methods. This has meant code like this: - -```java -MyCustomerOperators.operate(observable.map(...).filter(...).take(5)).map(...).subscribe() -``` - -In reality we want: - -```java -observable.map(...).filter(...).take(5).myCustomOperator().map(...).subscribe() -``` - -Using the newly added `lift` we can get quite close to this: - - -```java -observable.map(...).filter(...).take(5).lift(MyCustomOperator.operate()).map(...).subscribe() -``` - -Here is how the proposed `lift` method looks if all operators were applied with it: - -```java -Observable os = OBSERVABLE_OF_INTEGERS.lift(TAKE_5).lift(MAP_INTEGER_TO_STRING); -``` - -Along with the `lift` function comes a new `Operator` signature: - -```java -public interface Operator extends Func1, Subscriber> -``` - -All operator implementations in the `rx.operators` package will over time be migrated to this new signature. - -NOTE: Operators that have not yet been migrated do not work with synchronous unsubscribe. - - -##### 3) Simpler Operator Implementations - -The `lift` operator injects the necessary `Observer` and `Subscription` instances (via the new `Subscriber` type) and eliminates (for most use cases) the need for manual subscription management. Because the `Subscription` is available in-scope there are no awkward coding patterns needed for creating a `Subscription`, closing over it and returning and taking into account synchronous vs asynchronous. - -For example, the body of `fromIterable` is simply: - -```java -public void call(Subscriber o) { - for (T i : is) { - if (o.isUnsubscribed()) { - return; - } - o.onNext(i); - } - o.onCompleted(); -} -``` - -The `take` operator is: - -```java -public Subscriber call(final Subscriber child) { - final CompositeSubscription parent = new CompositeSubscription(); - if (limit == 0) { - child.onCompleted(); - parent.unsubscribe(); - } - - child.add(parent); - return new Subscriber(parent) { - - int count = 0; - boolean completed = false; - - @Override - public void onCompleted() { - if (!completed) { - child.onCompleted(); - } - } - - @Override - public void onError(Throwable e) { - if (!completed) { - child.onError(e); - } - } - - @Override - public void onNext(T i) { - if (!isUnsubscribed()) { - child.onNext(i); - if (++count >= limit) { - completed = true; - child.onCompleted(); - unsubscribe(); - } - } - } - - }; - } -``` - - -##### 4) Recursion/Loop Performance with Unsubscribe - -The `fromIterable` use case is 20x faster when implemented as a loop instead of recursive scheduler (see https://github.com/Netflix/RxJava/commit/a18b8c1a572b7b9509b7a7fe1a5075ce93657771). - -Several places we can remove recursive scheduling used originally for unsubscribe support and use a loop instead. - - - - - -#### Schedulers - - -Schedulers were greatly simplified to a design based around `Action1`. - -```java -public abstract class Scheduler { - public Subscription schedule(Action1 action); - public Subscription schedule(Action1 action, long delayTime, TimeUnit unit); - public Subscription schedulePeriodically(Action1 action, long initialDelay, long period, TimeUnit unit); - public final Subscription scheduleRecursive(final Action1 action) - public long now(); - public int degreeOfParallelism(); - - public static class Inner implements Subscription { - public abstract void schedule(Action1 action, long delayTime, TimeUnit unit); - public abstract void schedule(Action1 action); - public long now(); - } - - public static final class Recurse { - public final void schedule(); - public final void schedule(long delay, TimeUnit unit); - } -} -``` - -This design change originated from three findings: - -1) It was very easy to cause memory leaks or inadvertent parallel execution since the distinction between outer and inner scheduling was not obvious. - -To solve this the new design explicitly has the outer `Scheduler` and then `Scheduler.Inner` for recursion. - -2) The passing of state is not useful since scheduling over network boundaries with this model does not work. - -In this new design all state passing signatures have been removed. This was determined while implementing a `RemoteScheduler` that attempted to use `observeOn` to transition execution from one machine to another. This does not work because of the requirement for serializing/deserializing the state of the entire execution stack. Migration of work over the network has been bound to be better suited to explicit boundaries established by Subjects. Thus, the complications within the Schedulers are unnecessary. - -3) The number of overloads with different ways of doing the same things were confusing. - -This new design removes all but the essential and simplest methods. - -4) A scheduled task could not do work in a loop and easily be unsubscribed which generally meant less efficient recursive scheduling. - -This new design applies similar principles as done with `lift`/`create`/`OnSubscribe`/`Subscriber` and injects the `Subscription` via the `Inner` interface so a running task can check `isUnsubscribed()`. - - - -WIth this new design, the simplest execution of a single task is: - -```java -Schedulers.newThread().schedule(new Action1() { - - @Override - public void call(Inner inner) { - doWork(); - } - -}); -``` - -Recursion is easily invoked like this: - -```java -Schedulers.newThread().scheduleRecursive(new Action1() { - - @Override - public void call(Recurse recurse) { - doWork(); - // recurse until unsubscribed (the schedule will do nothing if unsubscribed) - recurse.schedule(); - } - - -}); -``` - -or like this if the outer and inner actions need different behavior: - -```java -Schedulers.newThread().schedule(new Action1() { - - @Override - public void call(Inner inner) { - doWork(); - // recurse until unsubscribed (the schedule will do nothing if unsubscribed) - inner.schedule(this); - } - -}); -``` - -The use of `Action1` on both the outer and inner levels makes it so recursion that refer to `this` and it works easily. - - -Similar to the new `lift`/`create` pattern with `Subscriber` the `Inner` is also a `Subscription` so it allows efficient loops with `unsubscribe` support: - -```java -Schedulers.newThread().schedule(new Action1() { - - @Override - public void call(Inner inner) { - while(!inner.isUnsubscribed()) { - doWork(); - } - } - -}); -``` - -An action can now `unsubscribe` the `Scheduler.Inner`: - -```java -Schedulers.newThread().schedule(new Action1() { - - @Override - public void call(Inner inner) { - while(!inner.isUnsubscribed()) { - int i = doOtherWork(); - if(i > 100) { - // an Action can cause the Scheduler to unsubscribe and stop - inner.unsubscribe(); - } - } - } - -}); -``` - -Typically just stopping is sufficient: - -```java -Schedulers.newThread().schedule(new Action1() { - - @Override - public void call(Inner inner) { - int i = doOtherWork(); - if (i < 10) { - // recurse until done 10 - inner.schedule(this); - } - } - -}); -``` - -but if other work in other tasks is being done and you want to unsubscribe conditionally you could: - -```java -Schedulers.newThread().schedule(new Action1() { - - @Override - public void call(Inner inner) { - int i = doOtherWork(); - if (i < 10) { - // recurse until done 10 - inner.schedule(this); - } else { - inner.unsubscribe(); - } - } - -}); -``` - -and the recursion can be delayed: - -```java -Schedulers.newThread().schedule(new Action1() { - - @Override - public void call(Inner inner) { - doWork(); - // recurse until unsubscribed ... but delay the recursion - inner.schedule(this, 500, TimeUnit.MILLISECONDS); - } - -}); -``` - -The same pattern works with the `Recurse` signature: - -```java -Schedulers.newThread().scheduleRecursive(new Action1() { - - @Override - public void call(Recurse recurse) { - doWork(); - // recurse until unsubscribed (the schedule will do nothing if unsubscribed) - recurse.schedule(500, TimeUnit.MILLISECONDS); - } - - -}); -``` - -The methods on the `Inner` never return a `Subscription` because they are always a single thread/event-loop/actor/etc and controlled by the `Subscription` returned by the initial `Scheduler.schedule` method. This is part of clarifying the contract. - -Thus an `unsubscribe` controlled from the outside would be done like this: - -```java -Subscription s = Schedulers.newThread().schedule(new Action1() { - - @Override - public void call(Inner inner) { - while(!inner.isUnsubscribed()) { - doWork(); - } - } - -}); - -// unsubscribe from outside -s.unsubscribe(); -``` - - - -#### Migration Path - -##### 1) Lift/OnSubscribe/Subscriber - -The `lift` function will not be used by most and is additive so will not affect backwards compatibility. The `Subscriber` type is also additive and for most use cases does not need to be used directly, the `Observer` interface can continue being used. - -The previous `create(OnSubscribeFunc f)` signature has been deprecated so code will work but now have warnings. Please begin migrating code as this will be deleted prior to the 1.0 release. - -Code such as this: - -```java -Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(Observer o) { - o.onNext(1); - o.onCompleted(); - return Subscriptions.empty(); - } -}); -``` - -should change to this: - -```java -Observable.create(new OnSubscribe() { - - @Override - public void call(Subscriber subscriber) { - subscriber.onNext(1); - subscriber.onCompleted(); - } -}); -``` - -If concurrency was being injected to allow unsubscribe support: - -```java -Observable.create(new OnSubscribeFunc() { - - @Override - public Subscription onSubscribe(final Observer o) { - final BooleanSubscription s = new BooleanSubscription(); - Thread t = new Thread(new Runnable() { - - @Override - public void run() { - int i = 0; - while (!s.isUnsubscribed()) { - o.onNext(i++); - } - } - - }); - t.start(); - return s; - } -}); -``` - -you may no longer need it and can implement like this instead: - -```java -Observable.create(new OnSubscribe() { - - @Override - public void call(Subscriber subscriber) { - int i = 0; - while (!subscriber.isUnsubscribed()) { - subscriber.onNext(i++); - } - } -}); -``` - -or if the concurreny is still desired you can simplify the `Subscription` management: - -```java -Observable.create(new OnSubscribe() { - - @Override - public void call(final Subscriber subscriber) { - Thread t = new Thread(new Runnable() { - - @Override - public void run() { - int i = 0; - while (!subscriber.isUnsubscribed()) { - subscriber.onNext(i++); - } - } - - }); - t.start(); - } -}); -``` - -or use `subscribeOn` which now works to make synchronous `Observables` async while supporting `unsubscribe` (this didn't work before): - -```java -Observable.create(new OnSubscribe() { - - @Override - public void call(Subscriber subscriber) { - int i = 0; - while (!subscriber.isUnsubscribed()) { - subscriber.onNext(i++); - } - } -}).subscribeOn(Schedulers.newThread()); -``` - - - -##### 2) Schedulers - -Custom `Scheduler` implementations will need to be re-implemented and any direct use of the `Scheduler` interface will also need to be updated. - - -##### 3) Subscription - -If you have custom `Subscription` implementations you will see they now need an `isUnsubscribed()` method. - -You can either add this method, or wrap your function using `Subscriptions.create` and it will handle the `isUnsubscribed` behavior and execute your function when `unsubscribe()` is called. - -It is recommended to use `Subscriptions.create` for most `Subscription` usage. - - - -#### The Future... - -We have most if not all operators from Rx.Net that we want or intend to port. We think we have got the `create`/`subscribe` signatures as we want and the `Subscription` and `Scheduler` interfaces are now clean. There is at least one more major topic related to back pressure that may result in signature change in a future release. Beyond that no further major signature changing work is expected prior to 1.0. - -We still need to improve on some of the `Subject` implementations still, particularly `ReplaySubject`. We are beginning to focus after this release on cleaning up all of the operator implementations, stabilizing, fixing bugs and performance tuning. - -As we get closer to 1.0 there will be a release that focused on deleting all deprecated methods so it is suggested to start migrating off of them. - -We appreciate your usage, feedback and contributions and hope the library is creating value for you! - - -#### Pull Requests - - -* [Pull 767](https://github.com/Netflix/RxJava/pull/767) Zip fix for multiple onCompleted and moved unsubscribe outside the lock. -* [Pull 770](https://github.com/Netflix/RxJava/pull/770) Bind Operator -* [Pull 778](https://github.com/Netflix/RxJava/pull/778) Fix zip race condition -* [Pull 784](https://github.com/Netflix/RxJava/pull/784) Lift and Observer+Subscription -* [Pull 793](https://github.com/Netflix/RxJava/pull/793) Observer + Subscriber -* [Pull 796](https://github.com/Netflix/RxJava/pull/796) Add Subscription.isUnsubscribed() -* [Pull 797](https://github.com/Netflix/RxJava/pull/797) Scheduler Outer/Inner [Preview] -* [Pull 805](https://github.com/Netflix/RxJava/pull/805) Fix CompositeException -* [Pull 785](https://github.com/Netflix/RxJava/pull/785) Reimplement Zip Operator Using Lift [Preview] -* [Pull 814](https://github.com/Netflix/RxJava/pull/814) RunAsync method for outputting multiple values -* [Pull 812](https://github.com/Netflix/RxJava/pull/812) Fixed OperationSubscribeOn so OperationConditionalsTest works again. -* [Pull 816](https://github.com/Netflix/RxJava/pull/816) One global onCompleted object -* [Pull 818](https://github.com/Netflix/RxJava/pull/818) CompositeSubscription memory reduction -* [Pull 817](https://github.com/Netflix/RxJava/pull/817) Scala Scheduler Bindings Fix -* [Pull 819](https://github.com/Netflix/RxJava/pull/819) CompositeSubscription performance increase -* [Pull 781](https://github.com/Netflix/RxJava/pull/781) Fixed buglet in join binding, simplified types -* [Pull 783](https://github.com/Netflix/RxJava/pull/783) Implement some Android UI related operators -* [Pull 821](https://github.com/Netflix/RxJava/pull/821) Update to use Subscriber/Subscriptions.create -* [Pull 826](https://github.com/Netflix/RxJava/pull/826) Return wrapped Subscription -* [Pull 824](https://github.com/Netflix/RxJava/pull/824) Set setDaemon on NewThreadScheduler -* [Pull 828](https://github.com/Netflix/RxJava/pull/828) Repeat Operator -* [Pull 827](https://github.com/Netflix/RxJava/pull/827) Fixed cut & paster error in io scheduler -* [Pull 833](https://github.com/Netflix/RxJava/pull/833) Take operator was breaking the unsubscribe chain -* [Pull 822](https://github.com/Netflix/RxJava/pull/822) Reimplement 'subscribeOn' using 'lift' -* [Pull 832](https://github.com/Netflix/RxJava/pull/832) Issue #831 Fix for OperationJoin race condition -* [Pull 834](https://github.com/Netflix/RxJava/pull/834) Update clojure for 0.17 -* [Pull 839](https://github.com/Netflix/RxJava/pull/839) Error Handling: OnErrorNotImplemented and java.lang.Error -* [Pull 838](https://github.com/Netflix/RxJava/pull/838) Make Scala OnCompleted Notification an object -* [Pull 837](https://github.com/Netflix/RxJava/pull/837) Perf with JMH -* [Pull 841](https://github.com/Netflix/RxJava/pull/841) Range OnSubscribe -* [Pull 842](https://github.com/Netflix/RxJava/pull/842) Test Unsubscribe -* [Pull 845](https://github.com/Netflix/RxJava/pull/845) Fix problem with Subscription -* [Pull 847](https://github.com/Netflix/RxJava/pull/847) Various Changes While Fixing GroupBy -* [Pull 849](https://github.com/Netflix/RxJava/pull/849) Add 'Fragment-Host' to rxjava-contrib modules for OSGi -* [Pull 851](https://github.com/Netflix/RxJava/pull/851) Reimplement the timeout operator and fix timeout bugs -* [Pull 846](https://github.com/Netflix/RxJava/pull/846) Added overloaded createRequest method that takes an HttpContext instance -* [Pull 777](https://github.com/Netflix/RxJava/pull/777) Fixed testSingleSourceManyIterators -* [Pull 852](https://github.com/Netflix/RxJava/pull/852) rxjava-debug -* [Pull 853](https://github.com/Netflix/RxJava/pull/853) StringObservable Update -* [Pull 763](https://github.com/Netflix/RxJava/pull/763) Added support for custom functions in combineLatest. -* [Pull 854](https://github.com/Netflix/RxJava/pull/854) The onCreate hook disappeared -* [Pull 857](https://github.com/Netflix/RxJava/pull/857) Change Lift to use rx.Observable.Operator -* [Pull 859](https://github.com/Netflix/RxJava/pull/859) Add 'Fragment-Host' to rxjava-contrib/debug module for OSGi -* [Pull 860](https://github.com/Netflix/RxJava/pull/860) Fixing the generics for merge and lift -* [Pull 863](https://github.com/Netflix/RxJava/pull/863) Optimize SwingMouseEventSource.fromRelativeMouseMotion -* [Pull 862](https://github.com/Netflix/RxJava/pull/862) Update the timeout docs -* [Pull 790](https://github.com/Netflix/RxJava/pull/790) Convert to scan to use lift -* [Pull 866](https://github.com/Netflix/RxJava/pull/866) Update OperationScan to OperatorScan -* [Pull 870](https://github.com/Netflix/RxJava/pull/870) Add the selector variants of timeout in RxScala -* [Pull 874](https://github.com/Netflix/RxJava/pull/874) Update CompositeSubscriptionTest.java -* [Pull 869](https://github.com/Netflix/RxJava/pull/869) subscribeOn + groupBy -* [Pull 751](https://github.com/Netflix/RxJava/pull/751) Provide Observable.timestamp(Scheduler) to be used in the tests. -* [Pull 878](https://github.com/Netflix/RxJava/pull/878) Scheduler.scheduleRecursive -* [Pull 877](https://github.com/Netflix/RxJava/pull/877) Correct synchronization guard in groupByUntil -* [Pull 880](https://github.com/Netflix/RxJava/pull/880) Force ViewObservable be subscribed and unsubscribed in the UI thread -* [Pull 887](https://github.com/Netflix/RxJava/pull/887) Remove Bad Filter Logic -* [Pull 890](https://github.com/Netflix/RxJava/pull/890) Split SubscribeOn into SubscribeOn/UnsubscribeOn -* [Pull 891](https://github.com/Netflix/RxJava/pull/891) Eliminate rx.util.* dumping grounds -* [Pull 881](https://github.com/Netflix/RxJava/pull/881) Lift Performance -* [Pull 893](https://github.com/Netflix/RxJava/pull/893) Change Parallel to use Long instead of Int -* [Pull 894](https://github.com/Netflix/RxJava/pull/894) Synchronized Operator Check for isTerminated -* [Pull 885](https://github.com/Netflix/RxJava/pull/885) Fixed an issue with the from(Reader) added a bunch of unit tests. -* [Pull 896](https://github.com/Netflix/RxJava/pull/896) removing java 7 dep -* [Pull 883](https://github.com/Netflix/RxJava/pull/883) Make Subscriptions of SwingObservable thread-safe -* [Pull 895](https://github.com/Netflix/RxJava/pull/895) Rewrite OperationObserveFromAndroidComponent to OperatorObserveFromAndroid -* [Pull 892](https://github.com/Netflix/RxJava/pull/892) onErrorFlatMap + OnErrorThrowable -* [Pull 898](https://github.com/Netflix/RxJava/pull/898) Handle illegal errors thrown from plugin -* [Pull 901](https://github.com/Netflix/RxJava/pull/901) GroupBy Unit Test from #900 -* [Pull 902](https://github.com/Netflix/RxJava/pull/902) Fixed NullPointerException that may happen on timeout -* [Pull 903](https://github.com/Netflix/RxJava/pull/903) Scheduler.Recurse fields should be private -* [Pull 904](https://github.com/Netflix/RxJava/pull/904) Merge: Unsubscribe Completed Inner Observables -* [Pull 905](https://github.com/Netflix/RxJava/pull/905) RxJavaSchedulers Plugin -* [Pull 909](https://github.com/Netflix/RxJava/pull/909) Scheduler Plugin Refactor -* [Pull 910](https://github.com/Netflix/RxJava/pull/910) Remove groupBy with selector -* [Pull 918](https://github.com/Netflix/RxJava/pull/918) Operator: doOnTerminate -* [Pull 919](https://github.com/Netflix/RxJava/pull/919) BugFix: Zip Never Completes When Zero Observables -* [Pull 920](https://github.com/Netflix/RxJava/pull/920) Delete Deprecated onSubscribeStart That Doesn't Work -* [Pull 922](https://github.com/Netflix/RxJava/pull/922) Changes made while integrating it with our internal system -* [Pull 924](https://github.com/Netflix/RxJava/pull/924) Localized Operator Error Handling -* [Pull 925](https://github.com/Netflix/RxJava/pull/925) Rxjava clojure bindings final -* [Pull 926](https://github.com/Netflix/RxJava/pull/926) TestSubscriber: Default onError and Terminal Latch Behavior -* [Pull 927](https://github.com/Netflix/RxJava/pull/927) TestSubscriber lastSeenThread -* [Pull 936](https://github.com/Netflix/RxJava/pull/936) Skip fixed -* [Pull 942](https://github.com/Netflix/RxJava/pull/942) MathObservable -* [Pull 944](https://github.com/Netflix/RxJava/pull/944) OperationRetry -> OperatorRetry -* [Pull 945](https://github.com/Netflix/RxJava/pull/945) refactor the debug hooks before they become a breaking change. -* [Pull 934](https://github.com/Netflix/RxJava/pull/934) add Observable.startWith(Observable) method and unit test -* [Pull 929](https://github.com/Netflix/RxJava/pull/929) correct link to maven search -* [Pull 923](https://github.com/Netflix/RxJava/pull/923) Observable creation from Subscriber[T]=>Unit for Scala -* [Pull 931](https://github.com/Netflix/RxJava/pull/931) A number of improvements to OperatorObserveFromAndroidComponent -* [Pull 950](https://github.com/Netflix/RxJava/pull/950) Add support for Eclipse PDE - - -### Version 0.16.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.16.1%22)) ### - -* [Pull 730](https://github.com/Netflix/RxJava/pull/730) Improve Error Handling and Stacktraces When Unsubscribe Fails -* [Pull 720](https://github.com/Netflix/RxJava/pull/720) Added `Observable.timeout` wrappers to scala adapter -* [Pull 731](https://github.com/Netflix/RxJava/pull/731) Fix non-deterministic unit test -* [Pull 742](https://github.com/Netflix/RxJava/pull/742) Build with Gradle 1.10 -* [Pull 718](https://github.com/Netflix/RxJava/pull/718) Merge overloads -* [Pull 733](https://github.com/Netflix/RxJava/pull/733) Buffer with Observable boundary -* [Pull 734](https://github.com/Netflix/RxJava/pull/734) Delay with subscription and item delaying observables -* [Pull 735](https://github.com/Netflix/RxJava/pull/735) Window with Observable boundary -* [Pull 736](https://github.com/Netflix/RxJava/pull/736) MergeMap with Iterable and resultSelector overloads -* [Pull 738](https://github.com/Netflix/RxJava/pull/738) Publish and PublishLast overloads -* [Pull 739](https://github.com/Netflix/RxJava/pull/739) Debounce with selector -* [Pull 740](https://github.com/Netflix/RxJava/pull/740) Timeout with selector overloads -* [Pull 745](https://github.com/Netflix/RxJava/pull/745) Fixed `switch` bug -* [Pull 741](https://github.com/Netflix/RxJava/pull/741) Zip with iterable, removed old aggregator version and updated tests -* [Pull 749](https://github.com/Netflix/RxJava/pull/749) Separated Android test code from source -* [Pull 732](https://github.com/Netflix/RxJava/pull/732) Ported groupByUntil function to scala-adapter - - -### Version 0.16.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.16.0%22)) ### - - -This is a significant release with the following changes: - -- Refactor of Subjects and Subscriptions to non-blocking implementations -- Many bug fixes, new operators and behavior changes to match Rx.Net. -- Deprecation of some operators due to renaming or eliminating duplicates -- The `rx.concurrency` package has been renamed to `rx.schedulers`. Existing classes still remain in `rx.concurrency` but are deprecated. Use of `rx.concurrency` should be migrated to `rx.schedulers` as these deprecated classes will be removed in a future release. -- Breaking changes to Scala bindings. See [Release Notes](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/ReleaseNotes.md) for details. -- New modules: rxjava-string, rxjava-async-util and rxjava-computation-expressions for operators deemed not applicable to the core library. - ---- - -* [Pull 516](https://github.com/Netflix/RxJava/pull/516) rxjava-string module with StringObservable -* [Pull 533](https://github.com/Netflix/RxJava/pull/533) Operator: ToAsync -* [Pull 535](https://github.com/Netflix/RxJava/pull/535) Fix compilation errors due to referencing the Android support library directly -* [Pull 545](https://github.com/Netflix/RxJava/pull/545) Fixed Zip issue with infinite streams -* [Pull 539](https://github.com/Netflix/RxJava/pull/539) Zipping a finite and an infinite Observable -* [Pull 541](https://github.com/Netflix/RxJava/pull/541) Operator: SkipUntil -* [Pull 537](https://github.com/Netflix/RxJava/pull/537) Add scala adapters for doOnEach operator -* [Pull 560](https://github.com/Netflix/RxJava/pull/560) Add type variances for doOnEach actions -* [Pull 562](https://github.com/Netflix/RxJava/pull/562) Scala Adaptor Improvements -* [Pull 563](https://github.com/Netflix/RxJava/pull/563) Operator: GroupByUntil -* [Pull 561](https://github.com/Netflix/RxJava/pull/561) Revised Approach to Creating Observables in Scala -* [Pull 565](https://github.com/Netflix/RxJava/pull/565) Operator: GroupJoin v2 -* [Pull 567](https://github.com/Netflix/RxJava/pull/567) Operator: Timestamp with Scheduler -* [Pull 568](https://github.com/Netflix/RxJava/pull/568) Use lock free strategy for several Subscription implementations -* [Pull 571](https://github.com/Netflix/RxJava/pull/571) Operator: Sample with Observable v2 -* [Pull 572](https://github.com/Netflix/RxJava/pull/572) Multiple Subscriptions to ObserveOn -* [Pull 573](https://github.com/Netflix/RxJava/pull/573) Removed Opening and Closing historical artifacts -* [Pull 575](https://github.com/Netflix/RxJava/pull/575) Operator: SequenceEqual reimplementation -* [Pull 587](https://github.com/Netflix/RxJava/pull/587) Operator: LongCount -* [Pull 586](https://github.com/Netflix/RxJava/pull/586) Fix Concat to allow multiple observers -* [Pull 598](https://github.com/Netflix/RxJava/pull/598) New Scala Bindings -* [Pull 596](https://github.com/Netflix/RxJava/pull/596) Fix for buffer not stopping when unsubscribed -* [Pull 576](https://github.com/Netflix/RxJava/pull/576) Operators: Timer and Delay -* [Pull 593](https://github.com/Netflix/RxJava/pull/593) Lock-free subscriptions -* [Pull 599](https://github.com/Netflix/RxJava/pull/599) Refactor rx.concurrency to rx.schedulers -* [Pull 600](https://github.com/Netflix/RxJava/pull/600) BugFix: Replay Subject -* [Pull 594](https://github.com/Netflix/RxJava/pull/594) Operator: Start -* [Pull 604](https://github.com/Netflix/RxJava/pull/604) StringObservable.join -* [Pull 609](https://github.com/Netflix/RxJava/pull/609) Operation: Timer -* [Pull 612](https://github.com/Netflix/RxJava/pull/612) Operation: Replay (overloads) -* [Pull 628](https://github.com/Netflix/RxJava/pull/628) BugFix: MergeDelayError Synchronization -* [Pull 602](https://github.com/Netflix/RxJava/pull/602) BugFix: ObserveOn Subscription leak -* [Pull 631](https://github.com/Netflix/RxJava/pull/631) Make NewThreadScheduler create Daemon threads -* [Pull 651](https://github.com/Netflix/RxJava/pull/651) Subjects Refactor - Non-Blocking, Common Abstraction, Performance -* [Pull 661](https://github.com/Netflix/RxJava/pull/661) Subscriptions Rewrite -* [Pull 520](https://github.com/Netflix/RxJava/pull/520) BugFix: blocking/non-blocking `first` -* [Pull 621](https://github.com/Netflix/RxJava/pull/621) Scala: SerialSubscription & From -* [Pull 626](https://github.com/Netflix/RxJava/pull/626) BO.Latest, fixed: BO.next, BO.mostRecent, BO.toIterable -* [Pull 633](https://github.com/Netflix/RxJava/pull/633) BugFix: null in toList operator -* [Pull 635](https://github.com/Netflix/RxJava/pull/635) Conditional Operators -* [Pull 638](https://github.com/Netflix/RxJava/pull/638) Operations: DelaySubscription, TakeLast w/ time, TakeLastBuffer -* [Pull 659](https://github.com/Netflix/RxJava/pull/659) Missing fixes from the subject rewrite -* [Pull 688](https://github.com/Netflix/RxJava/pull/688) Fix SafeObserver handling of onComplete errors -* [Pull 690](https://github.com/Netflix/RxJava/pull/690) Fixed Scala bindings -* [Pull 693](https://github.com/Netflix/RxJava/pull/693) Kotlin M6.2 -* [Pull 689](https://github.com/Netflix/RxJava/pull/689) Removed ObserverBase -* [Pull 664](https://github.com/Netflix/RxJava/pull/664) Operation: AsObservable -* [Pull 697](https://github.com/Netflix/RxJava/pull/697) Operations: Skip, SkipLast, Take with time -* [Pull 698](https://github.com/Netflix/RxJava/pull/698) Operations: Average, Sum -* [Pull 699](https://github.com/Netflix/RxJava/pull/699) Operation: Repeat -* [Pull 701](https://github.com/Netflix/RxJava/pull/701) Operation: Collect -* [Pull 707](https://github.com/Netflix/RxJava/pull/707) Module: rxjava-async-util -* [Pull 708](https://github.com/Netflix/RxJava/pull/708) BugFix: combineLatest -* [Pull 712](https://github.com/Netflix/RxJava/pull/712) Fix Scheduler Memory Leaks -* [Pull 714](https://github.com/Netflix/RxJava/pull/714) Module: rxjava-computation-expressions -* [Pull 715](https://github.com/Netflix/RxJava/pull/715) Add missing type hint to clojure example -* [Pull 717](https://github.com/Netflix/RxJava/pull/717) Scala: Added ConnectableObservable -* [Pull 723](https://github.com/Netflix/RxJava/pull/723) Deprecate multiple arity ‘from’ -* [Pull 724](https://github.com/Netflix/RxJava/pull/724) Revert use of CurrentThreadScheduler for Observable.from -* [Pull 725](https://github.com/Netflix/RxJava/pull/725) Simpler computation/io naming for Schedulers -* [Pull 727](https://github.com/Netflix/RxJava/pull/727) ImmediateScheduler optimization for toObservableIterable - - -### Version 0.15.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.15.1%22)) ### - -This release should be additive functionality and bug fixes. - -* [Pull 510](https://github.com/Netflix/RxJava/pull/506) Operators: And, Then, When -* [Pull 514](https://github.com/Netflix/RxJava/pull/514) Operator: Join -* [Pull 525](https://github.com/Netflix/RxJava/pull/526) Operators: toMap/toMultiMap -* [Pull 510](https://github.com/Netflix/RxJava/pull/510) BugFix: Zip -* [Pull 512](https://github.com/Netflix/RxJava/pull/512) Scala Adaptor Details -* [Pull 512](https://github.com/Netflix/RxJava/pull/529) Scala fixes -* [Pull 508](https://github.com/Netflix/RxJava/pull/508) Empty subscribe -* [Pull 522](https://github.com/Netflix/RxJava/pull/522) Unsubscribe from takeLast -* [Pull 525](https://github.com/Netflix/RxJava/pull/525) BugFix: Handling of Terminal State for Behavior/Publish Subjects - -### Version 0.15.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.15.0%22)) ### - -This release contains a refactor of the Scala Bindings by @headinthebox that results in some breaking changes. -The previous solution ended up not working well in all cases for idiomatic Scala usage. Java/Scala interop has been changed and is no longer transparent so as to optimize for native Scala usage. -Read the [rxjava-scala README](https://github.com/Netflix/RxJava/tree/master/language-adaptors/rxjava-scala) for more information. - -* [Pull 503](https://github.com/Netflix/RxJava/pull/503) New Scala Bindings -* [Pull 502](https://github.com/Netflix/RxJava/pull/502) Fix ObserveOn and add ParallelMerge Scheduler overload -* [Pull 499](https://github.com/Netflix/RxJava/pull/499) ObserveOn Refactor -* [Pull 492](https://github.com/Netflix/RxJava/pull/492) Implement the scheduler overloads for Range, From, StartWith -* [Pull 496](https://github.com/Netflix/RxJava/pull/496) Add contravariant for min and max - -### Version 0.14.11 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.11%22)) ### - -* [Pull 486](https://github.com/Netflix/RxJava/pull/486) BugFix: AsyncSubject -* [Pull 483](https://github.com/Netflix/RxJava/pull/483) Tweaks to DoOnEach and added DoOnError/DoOnCompleted - -This has a very slight breaking change by removing one `doOnEach` overload. The version was not bumped from 0.14 to 0.15 as it is so minor and the offending method was just released in the previous version. - -### Version 0.14.10 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.10%22)) ### - -* [Pull 481](https://github.com/Netflix/RxJava/pull/481) Operator: Using -* [Pull 480](https://github.com/Netflix/RxJava/pull/480) BugFix: Emit an IllegalArgumentException instead of ArithmeticException if the observable is empty -* [Pull 479](https://github.com/Netflix/RxJava/pull/479) Operator: DoOnEach -* [Pull 478](https://github.com/Netflix/RxJava/pull/478) Operator: Min, MinBy, Max, MaxBy -* [Pull 463](https://github.com/Netflix/RxJava/pull/463) Add Timeout Overloads - -### Version 0.14.9 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.9%22)) ### - -* [Pull 477](https://github.com/Netflix/RxJava/pull/477) BugFix: CompositeSubscription -* [Pull 476](https://github.com/Netflix/RxJava/pull/476) BugFix: Don't emit null onComplete when no onNext received in AsyncSubject -* [Pull 474](https://github.com/Netflix/RxJava/pull/474) BugFix: Reduce an empty observable -* [Pull 474](https://github.com/Netflix/RxJava/pull/474) BugFix: non-deterministic unit test -* [Pull 472](https://github.com/Netflix/RxJava/pull/472) BugFix: Issue 431 Unsubscribe with Schedulers.newThread -* [Pull 470](https://github.com/Netflix/RxJava/pull/470) Operator: Last - -### Version 0.14.8 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.8%22)) ### - -* [Pull 460](https://github.com/Netflix/RxJava/pull/460) Operator: Amb -* [Pull 466](https://github.com/Netflix/RxJava/pull/466) Refactor Unit Tests - -### Version 0.14.7 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.7%22)) ### - -* [Pull 459](https://github.com/Netflix/RxJava/pull/459) Fix multiple unsubscribe behavior -* [Pull 458](https://github.com/Netflix/RxJava/pull/458) rxjava-android: OperationObserveFromAndroidComponent -* [Pull 453](https://github.com/Netflix/RxJava/pull/453) Fix error handling in map operator -* [Pull 450](https://github.com/Netflix/RxJava/pull/450) Operator: TimeInterval -* [Pull 452](https://github.com/Netflix/RxJava/pull/451) Scheduler Overload of Just/Return Operator -* [Pull 433](https://github.com/Netflix/RxJava/pull/433) Fixes: Next Operator -* [Commit d64a8c5](https://github.com/Netflix/RxJava/commit/d64a8c5f73d8d1a5de1861e0d20f12609b408880) Update rxjava-apache-http to Apache HttpAsyncClient 4.0 GA - -### Version 0.14.6 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.6%22)) ### - -* [Pull 441](https://github.com/Netflix/RxJava/pull/441) Fixed the issue that 'take' does not call 'onError' -* [Pull 443](https://github.com/Netflix/RxJava/pull/443) OperationSwitch notify onComplete() too early. -* [Pull 434](https://github.com/Netflix/RxJava/pull/434) Timeout operator and SerialSubscription -* [Pull 447](https://github.com/Netflix/RxJava/pull/447) Caching the result of 'isInternalImplementation' - -### Version 0.14.5 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.5%22)) ### - -* [Pull 438](https://github.com/Netflix/RxJava/pull/438) Kotlin Language Adaptor - -### Version 0.14.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.4%22)) ### - -* [Issue 428](https://github.com/Netflix/RxJava/issues/428) Fix: buffer() using TimeAndSizeBasedChunks incorrectly forces thread into interrupted state -* [Pull 435](https://github.com/Netflix/RxJava/pull/435) rx-apache-http recognizes "Transfer-Encoding: chunked" as an HTTP stream -* [Pull 437](https://github.com/Netflix/RxJava/pull/437) Fixes: Scheduler and Merge - - -### Version 0.14.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.3%22)) ### - -* [Pull 407](https://github.com/Netflix/RxJava/pull/407) Operator: RefCount -* [Pull 410](https://github.com/Netflix/RxJava/pull/410) Operator: Contains -* [Pull 411](https://github.com/Netflix/RxJava/pull/411) Unit Test fix: update counter before triggering latch -* [Pull 413](https://github.com/Netflix/RxJava/pull/413) Fixed the issues of takeLast(items, 0) and null values -* [Pull 414](https://github.com/Netflix/RxJava/pull/414) Operator: SkipLast -* [Pull 415](https://github.com/Netflix/RxJava/pull/415) Operator: Empty with scheduler -* [Pull 416](https://github.com/Netflix/RxJava/pull/416) Operator: Throw with scheduler -* [Pull 420](https://github.com/Netflix/RxJava/pull/420) Scala Adaptor Improvements -* [Pull 422](https://github.com/Netflix/RxJava/pull/422) JRuby function wrapping support -* [Pull 424](https://github.com/Netflix/RxJava/pull/424) Operator: IgnoreElements -* [Pull 426](https://github.com/Netflix/RxJava/pull/426) PublishSubject ReSubscribe for publish().refCount() Behavior - -### Version 0.14.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.2%22)) ### - -* [Pull 403](https://github.com/Netflix/RxJava/pull/403) Operators: Cast and OfType -* [Pull 401](https://github.com/Netflix/RxJava/pull/401) Operator: DefaultIfEmpty -* [Pull 409](https://github.com/Netflix/RxJava/pull/409) Operator: Synchronize with object - -### Version 0.14.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.1%22)) ### - -* [Pull 402](https://github.com/Netflix/RxJava/pull/402) rxjava-apache-http improvements - -### Version 0.14.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.14.0%22)) ### - -Further progress to the Scala adaptor and a handful of new operators. - -Bump to 0.14.0 due to small breaking change to `distinct` operator removing overloaded methods with `Comparator`. These methods were added in 0.13.2 and determined to be incorrect. - -This release also includes a new contrib module, [rxjava-apache-http](https://github.com/Netflix/RxJava/tree/master/rxjava-contrib/rxjava-apache-http) that provides an Observable API to the Apache HttpAsyncClient. - -* [Pull 396](https://github.com/Netflix/RxJava/pull/396) Add missing methods to Scala Adaptor -* [Pull 390](https://github.com/Netflix/RxJava/pull/390) Operators: ElementAt and ElementAtOrDefault -* [Pull 398](https://github.com/Netflix/RxJava/pull/398) Operators: IsEmpty and Exists (instead of Any) -* [Pull 397](https://github.com/Netflix/RxJava/pull/397) Observable API for Apache HttpAsyncClient 4.0 -* [Pull 400](https://github.com/Netflix/RxJava/pull/400) Removing `comparator` overloads of `distinct` - -### Version 0.13.5 - -* Upload to Sonatype failed so version skipped - -### Version 0.13.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.13.4%22)) ### - -* [Pull 393](https://github.com/Netflix/RxJava/pull/393) Parallel Operator & ObserveOn/ScheduledObserver Fixes -* [Pull 394](https://github.com/Netflix/RxJava/pull/394) Change Interval and Sample default Scheduler -* [Pull 391](https://github.com/Netflix/RxJava/pull/391) Fix OSGI support for rxjava-scala - -### Version 0.13.3 - -* Upload to Sonatype failed so version skipped - -### Version 0.13.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.13.2%22)) ### - -* [Pull 389](https://github.com/Netflix/RxJava/pull/389) Scala Adaptor Improvements -* [Pull 382](https://github.com/Netflix/RxJava/pull/382) Removing deprecated RxImplicits from rxjava-scala -* [Pull 381](https://github.com/Netflix/RxJava/pull/381) Operator: mapWithIndex -* [Pull 380](https://github.com/Netflix/RxJava/pull/380) Implemented `distinct` and `distinctUntilChanged` variants using a comparator -* [Pull 379](https://github.com/Netflix/RxJava/pull/379) Make `interval` work with multiple subscribers - -### Version 0.13.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.13.1%22)) ### - -This release includes a new Scala adaptor as part of the effort from issue https://github.com/Netflix/RxJava/issues/336 pursuing idiomatic Scala support. - -* [Pull 376](https://github.com/Netflix/RxJava/pull/376) Idiomatic Scala Adaptor -* [Pull 375](https://github.com/Netflix/RxJava/pull/375) Operator: Distinct -* [Pull 374](https://github.com/Netflix/RxJava/pull/374) Operator: DistinctUntilChanged -* [Pull 373](https://github.com/Netflix/RxJava/pull/373) Fixes and Cleanup - -### Version 0.13.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.13.0%22)) ### - -This release has some minor changes related to varargs that could break backwards compatibility -if directly passing arrays but for most this release should not be breaking. - -* [Pull 354](https://github.com/Netflix/RxJava/pull/354) Operators: Count, Sum, Average -* [Pull 355](https://github.com/Netflix/RxJava/pull/355) Operators: skipWhile and skipWhileWithIndex -* [Pull 356](https://github.com/Netflix/RxJava/pull/356) Operator: Interval -* [Pull 357](https://github.com/Netflix/RxJava/pull/357) Operators: first and firstOrDefault -* [Pull 368](https://github.com/Netflix/RxJava/pull/368) Operators: Throttle and Debounce -* [Pull 371](https://github.com/Netflix/RxJava/pull/371) Operator: Retry -* [Pull 370](https://github.com/Netflix/RxJava/pull/370) Change zip method signature from Collection to Iterable -* [Pull 369](https://github.com/Netflix/RxJava/pull/369) Generics Improvements: co/contra-variance -* [Pull 361](https://github.com/Netflix/RxJava/pull/361) Remove use of varargs from API - -### Version 0.12.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.12.2%22)) ### - -* [Pull 352](https://github.com/Netflix/RxJava/pull/352) Groovy Language Adaptor: Add Func5-9 and N to the wrapper - -### Version 0.12.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.12.1%22)) ### - -* [Pull 350](https://github.com/Netflix/RxJava/pull/350) Swing module enhancements -* [Pull 351](https://github.com/Netflix/RxJava/pull/351) Fix Observable.window static/instance bug - -### Version 0.12.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.12.0%22)) ### - -This version adds to the static typing changes in 0.11 and adds covariant/contravariant typing via super/extends generics. - -Additional cleanup was done, particularly related to `BlockingObservable`. Also the `window` operator was added. - -The largest breaking change is that `Observable.create` now accepts an `OnSubscribeFunc` rather than a `Func1`. - -This means that instead of this: - -```java -public static Observable create(Func1, ? extends Subscription> func) -``` - -it is now: - -```java -public static Observable create(OnSubscribeFunc func) -``` - -This was done to simplify the usage of `Observable.create` which was already verbose but made far worse by the `? super` generics. - -For example, instead of writing this: - -```java -Observable.create(new Func1, Subscription>() { - /// body here -} -``` - -it is now written as: - -```java -Observable.create(new OnSubscribeFunc() { - /// body here -} -``` - -* [Pull 343](https://github.com/Netflix/RxJava/pull/343) Covariant Support with super/extends and `OnSubscribeFunc` as type for `Observable.create` -* [Pull 337](https://github.com/Netflix/RxJava/pull/337) Operator: `window` -* [Pull 348](https://github.com/Netflix/RxJava/pull/348) Rename `switchDo` to `switchOnNext` (deprecate `switchDo` for eventual deletion) -* [Pull 348](https://github.com/Netflix/RxJava/pull/348) Delete `switchDo` instance method in preference for static -* [Pull 346](https://github.com/Netflix/RxJava/pull/346) Remove duplicate static methods from `BlockingObservable` -* [Pull 346](https://github.com/Netflix/RxJava/pull/346) `BlockingObservable` no longer extends from `Observable` -* [Pull 345](https://github.com/Netflix/RxJava/pull/345) Remove unnecessary constructor from `Observable` - -### Version 0.11.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.11.2%22)) ### - -* [Commit ccf53e8]( https://github.com/Netflix/RxJava/commit/ccf53e84835d99136cce80a4c508bae787d5da45) Update to Scala 2.10.2 - -### Version 0.11.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.11.1%22)) ### - -* [Pull 325](https://github.com/Netflix/RxJava/pull/325) Clojure: Preserve metadata on fn and action macros - -### Version 0.11.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.11.0%22)) ### - -This is a major refactor of rxjava-core and the language adaptors. - -Note that there are *breaking changes* in this release. Details are below. - -After this refactor it is expected that the API will settle down and allow us to stabilize towards a 1.0 release. - -* [Pull 332](https://github.com/Netflix/RxJava/pull/332) Refactor Core to be Statically Typed - -RxJava was written from the beginning to target the JVM, not any specific language. - -As a side-effect of Java not having lambdas/clojures yet (and other considerations), Netflix used dynamic languages with it predominantly for the year of its existence prior to open sourcing. - -To bridge the rxjava-core written in Java with the various languages a FunctionalLanguageAdaptor was registered at runtime for each language of interest. - -To enable these language adaptors methods are overloaded with `Object` in the API since `Object` is the only super-type that works across all languages for their various implementations of lambdas and closures. - -This downside of this has been that it breaks static typing for Java, Scala and other statically-typed languages. More can be read on this issue and discussion of the subject here: https://groups.google.com/forum/#!topic/rxjava/bVZoKSsb1-o - -This release: - -- removes all `Object` overload methods from rxjava-core so it is statically typed -- removes dynamic FunctionalLanguageAdaptors -- uses idiomatic approaches for each language adaptor - - Java core is statically typed and has no knowledge of other languages - - Scala uses implicits - - Groovy uses an ExtensionModule - - Clojure adds a new macro ([NOTE: this requires code changes](https://github.com/Netflix/RxJava/tree/master/language-adaptors/rxjava-clojure#basic-usage)) - - JRuby has been temporarily disabled (discussing new implementation at https://github.com/Netflix/RxJava/issues/320) -- language supports continue to be additive - - the rxjava-core will always be required and then whichever language modules are desired such as rxjava-scala, rxjava-clojure, rxjava-groovy are added to the classpath -- deletes deprecated methods -- deletes redundant static methods on `Observable` that cluttered the API and in some cases caused dynamic languages trouble choosing which method to invoke -- deletes redundant methods on `Scheduler` that gave dynamic languages a hard time choosing which method to invoke - -The benefits of this are: - -1) Everything is statically typed so compile-time checks for Java, Scala, etc work correctly -2) Method dispatch is now done via native Java bytecode using types rather than going via `Object` which then has to do a lookup in a map. Memoization helped with the performance but each method invocation still required looking in a map for the correct adaptor. With this approach the appropriate methods will be compiled into the `rx.Observable` class to correctly invoke the right adaptor without lookups. -3) Interaction from each language should work as expected idiomatically for that language. - -Further history on the various discussions and different attempts at solutions can be seen at https://github.com/Netflix/RxJava/pull/304, https://github.com/Netflix/RxJava/issues/204 and https://github.com/Netflix/RxJava/issues/208 - - -### Version 0.10.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.10.1%22)) ### - -A new contrib module for Android: https://github.com/Netflix/RxJava/tree/master/rxjava-contrib/rxjava-android - -* [Pull 318](https://github.com/Netflix/RxJava/pull/318) rxjava-android module with Android Schedulers - -### Version 0.10.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.10.0%22)) ### - -This release includes a breaking change as it changes `onError(Exception)` to `onError(Throwable)`. This decision was made via discussion at https://github.com/Netflix/RxJava/issues/296. - -Any statically-typed `Observer` implementations with `onError(Exception)` will need to be updated to `onError(Throwable)` when moving to this version. - -* [Pull 312](https://github.com/Netflix/RxJava/pull/312) Fix for OperatorOnErrorResumeNextViaObservable and async Resume -* [Pull 314](https://github.com/Netflix/RxJava/pull/314) Map Error Handling -* [Pull 315](https://github.com/Netflix/RxJava/pull/315) Change onError(Exception) to onError(Throwable) - Issue #296 - -### Version 0.9.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.9.2%22)) ### - -* [Pull 308](https://github.com/Netflix/RxJava/pull/308) Ensure now() is always updated in TestScheduler.advanceTo/By -* [Pull 281](https://github.com/Netflix/RxJava/pull/281) Operator: Buffer - -### Version 0.9.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.9.1%22)) ### - -* [Pull 303](https://github.com/Netflix/RxJava/pull/303) CombineLatest -* [Pull 290](https://github.com/Netflix/RxJava/pull/290) Zip overload with FuncN -* [Pull 302](https://github.com/Netflix/RxJava/pull/302) NPE fix when no package on class -* [Pull 284](https://github.com/Netflix/RxJava/pull/284) GroupBy fixes (items still [oustanding](https://github.com/Netflix/RxJava/issues/282)) -* [Pull 288](https://github.com/Netflix/RxJava/pull/288) PublishSubject concurrent modification fixes -* [Issue 198](https://github.com/Netflix/RxJava/issues/198) Throw if no onError handler specified -* [Issue 278](https://github.com/Netflix/RxJava/issues/278) Subscribe argument validation -* Javadoc improvements and many new marble diagrams - -### Version 0.9.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.9.0%22)) ### - -This release includes breaking changes that move all blocking operators (such as `single`, `last`, `forEach`) to `BlockingObservable`. - -This means `Observable` has only non-blocking operators on it. The blocking operators can now be accessed via `.toBlockingObservable()` or `BlockingObservable.from(observable)`. - -Notes and link to the discussion of this change can be found at https://github.com/Netflix/RxJava/pull/272. - -* [Pull 272](https://github.com/Netflix/RxJava/pull/272) Move blocking operators into BlockingObservable -* [Pull 273](https://github.com/Netflix/RxJava/pull/273) Fix Concat (make non-blocking) -* [Issue 13](https://github.com/Netflix/RxJava/issues/13) Operator: Switch -* [Pull 274](https://github.com/Netflix/RxJava/pull/274) Remove SLF4J dependency (RxJava is now a single jar with no dependencies) - -### Version 0.8.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.8.4%22)) ### - -* [Pull 269](https://github.com/Netflix/RxJava/pull/269) (Really) Fix concurrency bug in ScheduledObserver - -### Version 0.8.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.8.3%22)) ### - -* [Pull 268](https://github.com/Netflix/RxJava/pull/268) Fix concurrency bug in ScheduledObserver - -### Version 0.8.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.8.2%22)) ### - -* [Issue 74](https://github.com/Netflix/RxJava/issues/74) Operator: Sample -* [Issue 93](https://github.com/Netflix/RxJava/issues/93) Operator: Timestamp -* [Pull 253](https://github.com/Netflix/RxJava/pull/253) Fix multiple subscription bug on operation filter -* [Pull 254](https://github.com/Netflix/RxJava/pull/254) SwingScheduler (new rxjava-swing module) -* [Pull 256](https://github.com/Netflix/RxJava/pull/256) BehaviorSubject -* [Pull 257](https://github.com/Netflix/RxJava/pull/257) Improved scan, reduce, aggregate -* [Pull 262](https://github.com/Netflix/RxJava/pull/262) SwingObservable (new rxjava-swing module) -* [Pull 264](https://github.com/Netflix/RxJava/pull/263) Publish, Replay and Cache Operators -* -### Version 0.8.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.8.1%22)) ### - -* [Pull 250](https://github.com/Netflix/RxJava/pull/250) AsyncSubject -* [Pull 252](https://github.com/Netflix/RxJava/pull/252) ToFuture -* [Pull 246](https://github.com/Netflix/RxJava/pull/246) Scheduler.schedulePeriodically -* [Pull 247](https://github.com/Netflix/RxJava/pull/247) flatMap aliased to mapMany - -### Version 0.8.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.8.0%22)) ### - -This is a breaking (non-backwards compatible) release that updates the Scheduler implementation released in 0.7.0. - -See https://github.com/Netflix/RxJava/issues/19 for background, discussion and status of Schedulers. - -It is believed that the public signatures of Scheduler and related objects is now stabilized but ongoing feedback and review by the community could still result in changes. - -* [Issue 19](https://github.com/Netflix/RxJava/issues/19) Schedulers improvements, changes and additions -* [Issue 202](https://github.com/Netflix/RxJava/issues/202) Fix Concat bugs -* [Issue 65](https://github.com/Netflix/RxJava/issues/65) Multicast -* [Pull 218](https://github.com/Netflix/RxJava/pull/218) ReplaySubject - -### Version 0.7.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.7.0%22)) ### - -This release adds the foundations of Rx Schedulers. - -There are still open questions, portions not implemented and assuredly bugs and behavior we didn't understand and thus implemented wrong. - -Please provide bug reports, pull requests or feedback to help us on the road to version 1.0 and get schedulers implemented correctly. - -See https://github.com/Netflix/RxJava/issues/19#issuecomment-15979582 for some known open questions that we could use help answering. - -* [Issue 19](https://github.com/Netflix/RxJava/issues/19) Schedulers - -### Version 0.6.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.6.3%22)) ### - -* [Pull 224](https://github.com/Netflix/RxJava/pull/224) RxJavaObservableExecutionHook - -### Version 0.6.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.6.2%22)) ### - -* [Issue 101](https://github.com/Netflix/RxJava/issues/101) Operator: Where (alias to filter) -* [Pull 197](https://github.com/Netflix/RxJava/pull/197) TakeWhile observables do not properly complete -* [Issue 21](https://github.com/Netflix/RxJava/issues/21) Operator: All -* [Pull 206](https://github.com/Netflix/RxJava/pull/206) Observable.toList breaks with multiple subscribers -* [Issue 29](https://github.com/Netflix/RxJava/issues/29) Operator: CombineLatest -* [Issue 211](https://github.com/Netflix/RxJava/issues/211) Remove use of JSR 305 and dependency on com.google.code.findbugs -* [Pull 212](https://github.com/Netflix/RxJava/pull/212) Operation take leaks errors -* [Pull 220](https://github.com/Netflix/RxJava/pull/220) TakeWhile protect calls to predicate -* [Pull 221](https://github.com/Netflix/RxJava/pull/221) Error Handling Improvements - User Provided Observers/Functions -* [Pull 201](https://github.com/Netflix/RxJava/pull/201) Synchronize Observer on OperationMerge -* [Issue 43](https://github.com/Netflix/RxJava/issues/43) Operator: Finally - -### Version 0.6.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.6.1%22)) ### - -* [Pull 190](https://github.com/Netflix/RxJava/pull/190) Fix generics issue with materialize() that prevented chaining - -### Version 0.6.0 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.6.0%22)) ### - -* [Issue 154](https://github.com/Netflix/RxJava/issues/154) Add OSGi manifest headers -* [Issue 173](https://github.com/Netflix/RxJava/issues/173) Subscription Utilities and Default Implementations -* [Pull 184](https://github.com/Netflix/RxJava/pull/184) Convert 'last' from non-blocking to blocking to match Rx.Net (see [Issue 57](https://github.com/Netflix/RxJava/issues/57)) - -*NOTE:* This is a version bump from 0.5 to 0.6 because [Issue 173](https://github.com/Netflix/RxJava/issues/173) and [Pull 184](https://github.com/Netflix/RxJava/pull/184) include breaking changes. - -These changes are being done in the goal of matching the [Rx.Net](https://rx.codeplex.com) implementation so breaking changes will be made prior to 1.0 on 0.x releases if necessary. - -It was found that the `last()` operator was implemented [incorrectly](https://github.com/Netflix/RxJava/issues/57) (non-blocking instead of blocking) so any use of `last()` on version 0.5.x should be changed to use `takeLast(1)`. Since the return type needed to change this could not be done via a deprecation. - -Also [removed](https://github.com/Netflix/RxJava/issues/173) were the `Observable.createSubscription`/`Observable.noOpSubscription` methods which are now on the rx.subscriptions.Subscriptions utility class as `Subscriptions.create`/`Subscriptions.empty`. These methods could have been deprecated rather than removed but since another breaking change was being done they were just cleanly changed as part of the pre-1.0 process. - - -### Version 0.5.5 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.5.5%22)) ### - -* [Issue 35](https://github.com/Netflix/RxJava/issues/35) Operator: Defer -* [Issue 37](https://github.com/Netflix/RxJava/issues/37) Operator: Dematerialize -* [Issue 50](https://github.com/Netflix/RxJava/issues/50) Operator: GetEnumerator (GetIterator) -* [Issue 64](https://github.com/Netflix/RxJava/issues/64) Operator: MostRecent -* [Issue 86](https://github.com/Netflix/RxJava/issues/86) Operator: TakeUntil - -### Version 0.5.4 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.5.4%22)) ### - -* [Issue 18](https://github.com/Netflix/RxJava/issues/18) Operator: ToIterable -* [Issue 58](https://github.com/Netflix/RxJava/issues/58) Operator: LastOrDefault -* [Issue 66](https://github.com/Netflix/RxJava/issues/66) Operator: Next -* [Issue 77](https://github.com/Netflix/RxJava/issues/77) Operator: Single and SingleOrDefault -* [Issue 164](https://github.com/Netflix/RxJava/issues/164) Range.createWithCount bugfix -* [Pull 161](https://github.com/Netflix/RxJava/pull/161) Build Status Badges and CI Integration - -### Version 0.5.3 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.5.3%22)) ### - -* [Issue 45](https://github.com/Netflix/RxJava/issues/45) Operator: ForEach -* [Issue 87](https://github.com/Netflix/RxJava/issues/87) Operator: TakeWhile -* [Pull 145](https://github.com/Netflix/RxJava/pull/145) IntelliJ IDEA support in Gradle build - -### Version 0.5.2 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.5.2%22)) ### - -* [Issue 68](https://github.com/Netflix/RxJava/issues/68) Operator: Range -* [Issue 76](https://github.com/Netflix/RxJava/issues/76) Operator: SequenceEqual -* [Issue 85](https://github.com/Netflix/RxJava/issues/85) Operator: TakeLast -* [Issue 139](https://github.com/Netflix/RxJava/issues/85) Plugin System -* [Issue 141](https://github.com/Netflix/RxJava/issues/85) Error Handler Plugin -* [Pull 134](https://github.com/Netflix/RxJava/pull/134) VideoExample in Clojure -* [Pull 135](https://github.com/Netflix/RxJava/pull/135) Idiomatic usage of import in ns macro in rx-examples. -* [Pull 136](https://github.com/Netflix/RxJava/pull/136) Add examples for jbundler and sbt - -### Version 0.5.1 ([Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.netflix.rxjava%22%20AND%20v%3A%220.5.1%22)) ### - -* variety of code cleanup commits -* [Pull 132](https://github.com/Netflix/RxJava/pull/132) Broke rxjava-examples mo diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 082524fc0c..98ae7225f3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,31 +1,28 @@ -# Contributing to RxJava +# Contributing to RxJava 3.x -If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request (on a branch other than `master` or `gh-pages`). +If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request targeting the branch `3.x`. When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. ## License -By contributing your code, you agree to license your contribution under the terms of the APLv2: https://github.com/Netflix/RxJava/blob/master/LICENSE +By contributing your code, you agree to license your contribution under the terms of the APLv2: https://github.com/ReactiveX/RxJava/blob/3.x/LICENSE All files are released with the Apache 2.0 license. If you are adding a new file it should have a header like this: ``` -/** - * 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 - * +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * 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. + * + * 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. */ ``` diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000000..5d2c6e53f3 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,13 @@ +Copyright (c) 2016-present, RxJava Contributors. + +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. \ No newline at end of file diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000000..a8410d492e --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,598 @@ +## RxJava v3 Design + +This document explains the terminology, principles, contracts, and other aspects of the design of RxJava v3. +Its intended audience is the implementers of the library. + +### Terminology & Definitions + +##### Interactive + +Producer obeys consumer-driven flow control. +Consumer manages capacity by requesting data. + + +##### Reactive + +Producer is in charge. Consumer has to do whatever it needs to keep up. + + +##### Hot + +When used to refer to a data source (such as an `Observable`), it means it does not have side-effects when subscribed to. + +For example, an `Observable` of mouse events. Subscribing to that `Observable` does not cause the mouse events, but starts receiving them. + +(Note: Yes, there are *some* side-effects of adding a listener, but they are inconsequential as far as the 'hot' usage is concerned). + + +##### Cold + +When used to refer to a data source (such as an `Observable`), it means it has side-effects when subscribed to. + +For example, an `Observable` of data from a remote API (such as an RPC call). Each time that `Observable` is subscribed to causes a new network call to occur. + + +##### Reactive/Push + +Producer is in charge. Consumer has to do whatever it needs to keep up. + +Examples: + +- `Observable` (RxJS, Rx.Net, RxJava v1.x without backpressure, RxJava v2). +- Callbacks (the producer calls the function at its convenience). +- IRQ, mouse events, IO interrupts. +- 3.x `Flowable` (with `request(n)` credit always granted faster or in larger quantity than producer). +- Reactive Streams `Publisher` (with `request(n)` credit always granted faster or in larger quantity than producer). +- Java 9 `Flow.Publisher` (with `request(n)` credit always granted faster than or in larger quantity than producer). + + +##### Synchronous Interactive/Pull + +Consumer is in charge. Producer has to do whatever it needs to keep up. + +Examples: + +- `Iterable`. +- 3.x/1.x `Observable` (without concurrency, producer and consumer on the same thread). +- 3.x `Flowable` (without concurrency, producer and consumer on the same thread). +- Reactive Streams `Publisher` (without concurrency, producer and consumer on the same thread). +- Java 9 `Flow.Publisher` (without concurrency, producer and consumer on the same thread). + + +##### Async Pull (Async Interactive) + +Consumer requests data when it wishes, and the data is then pushed when the producer wishes to. + +Examples: + +- `Future` & `Promise`. +- `Single` (lazy `Future`). +- 3.x `Flowable`. +- Reactive Streams `Publisher`. +- Java 9 `Flow.Publisher`. +- 1.x `Observable` (with backpressure). +- `AsyncEnumerable`/`AsyncIterable`. + +There is an overhead (performance and mental) for achieving this, which is why we also have the 3.x `Observable` without backpressure. + + +##### Flow Control + +Flow control is any mitigation strategy that a consumer applies to reduce the flow of data. + +Examples: + +- Controlling the production of data, such as with `Iterator.next` or `Subscription.request(n)`. +- Preventing the delivery of data, such as buffer, drop, sample/throttle, and debounce. + + +##### Eager + +Containing object immediately start work when it is created. + +Examples: + +- A `Future` once created has work being performed and represents the eventual value of that work. It can not be deferred once created. + + +##### Lazy + +Containing object does nothing until it is subscribed to or otherwise started. + +Examples: + +- `Observable.create` does not start any work until `Observable.subscribe` starts the work. + + +### RxJava & Related Types + +##### Observable + +Stream that supports async and synchronous push. It does *not* support interactive flow control (`request(n)`). + +Usable for: + +- Sync or async. +- Push. +- 0, 1, many or infinite items. + +Flow control support: + +- Buffering, sampling, throttling, windowing, dropping, etc. +- Temporal and count-based strategies. + +*Type Signature* + +```java +class Observable { + void subscribe(Observer observer); + + interface Observer { + void onNext(T t); + void onError(Throwable t); + void onComplete(); + void onSubscribe(Disposable d); + } +} +``` + +The rule for using this type signature is: + +> onSubscribe onNext* (onError | onComplete)? + + +##### Flowable + +Stream that supports async and synchronous push and pull. It supports interactive flow control (`request(n)`). + +Usable for: + +- Pull sources. +- Push Observables with backpressure strategy (i.e. `Observable.toFlowable(onBackpressureStrategy)`). +- Sync or async. +- 0, 1, many or infinite items. + +Flow control support: + +- Buffering, sampling, throttling, windowing, dropping, etc. +- Temporal and count-based strategies. +- `request(n)` consumer demand signal: + - For pull-based sources, this allows batched "async pull". + - For push-based sources, this allows backpressure signals to conditionally apply strategies (i.e. drop, first, buffer, sample, fail, etc.). + +You get a `Flowable` from: + +- Converting a Observable with a backpressure strategy. +- Create from sync/async `onSubscribe` API (which participate in backpressure semantics). + +*Type Signature* + +```java +class Flowable implements Flow.Publisher, io.reactivestreams.Publisher { + void subscribe(Subscriber subscriber); + + interface Subscription implements Flow.Subscription, io.reactivestreams.Subscription { + void cancel(); + void request(long n); + } +} +``` + +*NOTE: To support `Flow.Publisher` in Java 9+ without breaking Java 7+ compatibility, we want to use the [multi-release jar file support](http://openjdk.java.net/jeps/238).* + +The rule for using this type signature is: + +> onSubscribe onNext* (onError | onComplete)? + + +##### Single + +Lazy representation of a single response (lazy equivalent of `Future`/`Promise`). + +Usable for: + +- Pull sources. +- Push sources being windowed or flow controlled (such as `window(1)` or `take(1)`). +- Sync or async. +- 1 item. + +Flow control: + +- Not applicable (don't subscribe if the single response is not wanted). + +*Type Signature* + +```java +class Single { + void subscribe(Single.Subscriber subscriber); +} + +interface SingleSubscriber { + void onSuccess(T t); + void onError(Throwable t); + void onSubscribe(Disposable d); +} +``` + +> onSubscribe (onError | onSuccess)? + + +##### Completable + +Lazy representation of a unit of work that can complete or fail. + +- Semantic equivalent of `Observable.empty().doOnSubscribe()`. +- Alternative for scenarios often represented with types such as `Single` or `Observable`. + +Usable for: + +- Sync or async. +- 0 items. + +*Type Signature* + +```java +class Completable { + void subscribe(Completable.Subscriber subscriber); +} + +interface CompletableSubscriber { + void onComplete(); + void onError(Throwable t); + void onSubscribe(Disposable d); +} +``` + +> onSubscribe (onError | onComplete)? + +##### Observer + +Reactive consumer of events (without consumer-driven flow control). Involved in subscription lifecycle to allow unsubscription. + +##### Publisher + +Interactive producer of events (with flow control). Implemented by `Flowable`. + +[Reactive Streams producer](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/README.md#1-publisher-code) of data. + +##### Subscriber + +Interactive consumer of events (with consumer-driven flow control). Involved in subscription lifecycle to allow unsubscription. + +[Reactive Streams consumer](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/README.md#2-subscriber-code) of data. + +##### Subscription + +[Reactive Streams state](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/README.md#3-subscription-code) of subscription supporting flow control and cancellation. + +`Disposable` is a similar type used for lifecycle management on the `Observable` type without interactive flow control. + +##### Processor + +[Reactive Streams operator](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/README.md#4processor-code) for defining behavior between `Publisher` and `Subscriber`. It must obey the contracts of `Publisher` and `Subscriber`, meaning it is sequential, serialized, and must obey `request(n)` flow control. + +##### Subject + +A "hot", push-based data source that allows a producer to emit events to it and consumers to subscribe to events in a multicast manner. It is "hot" because consumers subscribing to it does not cause side-effects, or affect the data flow in any way. It is push-based and reactive because the producer is fully in charge. + +A `Subject` is used to decouple unsubscription. Termination is fully in the control of the producer. `onError` and `onComplete` are still terminal events. +`Subject`s are stateful and retain their terminal state (for replaying to all/future subscribers). + +Relation to Reactive Streams: + +- It can not implement Reactive Streams `Publisher` unless it is created with a default consumer-driven flow control strategy. +- It can not implement `Processor` since a `Processor` must compose `request(n)` which can not be done with multicasting or push. + +Here is an approach to converting from a `Subject` to Reactive Streams types by adding a default flow control strategy: + +```java +Subject s = PublishSubject.create(); +// convert to Publisher with backpressure strategy +Publisher p = s.toPublisher(onBackpressureStrategy); + +// now the request(n) semantics are handled by default +p.subscribe(subscriber1); +p.subscribe(subscriber2); +``` + +In this example, `subscriber1` and `subscriber2` can consume at different rates, `request(n)` will propagate to the provided `onBackpressureStrategy`, not the original `Subject` which can't propagate `request(n)` upstream. + + +##### Disposable + +A type representing work or resource that can be cancelled or disposed. + +Examples: + +- An `Observable.subscribe` passes a `Disposable` to the `Observable.onSubscribe` to allow the `Observer` to dispose of the subscription. +- A `Scheduler` returns a `Disposable` that you use for disposing of the `Scheduler`. + +`Subscription` is a similar type used for lifecycle management on the `Flowable` type with interactive flow control. + +##### Operator + +An operator follows a specific lifecycle (union of the producer/consumer contract). + +- It must propagate the `subscribe` event upstream (to the producer). +- It must obey the RxJava contract (serialize all events, `onError`/`onComplete` are terminal). +- If it has resources to cleanup it is responsible for watching `onError`, `onComplete`, and `cancel/dispose`, and doing the necessary cleanup. +- It must propagate the `cancel/dispose` upstream. + +In the addition of the previous rules, an operator for `Flowable`: + +- It must propagate/negotiate the `request(n)` event. + + +### Creation + +Unlike RxJava 1.x, 3.x base classes are to be abstract, stateless and generally no longer wrap an `onSubscribe` callback - this saves allocation in assembly time without limiting the expressiveness. Operator methods and standard factories still live as final on the base classes. + +Instead of the indirection of an `onSubscribe` and `lift`, operators are to be implemented by extending the base classes. For example, the `map` +operator will look like this: + +```java +public final class FlowableMap extends Flowable { + + final Flowable source; + + final Function mapper; + + public FlowableMap(Flowable source, Function mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + protected void subscribeActual(Subscriber subscriber) { + source.subscribe(new FlowableMapSubscriber(subscriber, mapper)); + } + + static final class FlowableMapSubscriber implements Subscriber, Subscription { + // ... + } +} +``` + +Since Java still doesn't have extension methods, "adding" more operators can only happen through helper methods such as `lift(C -> C)` and `compose(R -> P)` where `C` is the default consumer type (i.e. `rs.Subscriber`), `R` is the base type (i.e. `Flowable`) and `P` is the base interface (i.e. `rs.Publisher`). As before, the library itself may gain or lose standard operators and/or overloads through the same community process. + +In concert, `create(onSubscribe)` will not be available; standard operators extend the base types directly. The conversion of other RS-based libraries will happen through the `Flowable.wrap(Publisher)` static method. + +(*The unfortunate effect of `create` in 1.x was the ignorance of the Observable contract and beginner's first choice as an entry point. We can't eliminate this path since `rs.Publisher` is a single method functional interface that can be implemented just as badly.*) + +Therefore, new standard factory methods will try to address the common entry point requirements. + +The `Flowable` will contain the following `create` methods: + + - `create(SyncGenerator)`: safe, synchronous generation of signals, one-by-one. + - `create(AsyncOnSubscribe)`: batch-create signals based on request patterns. + - `create(Consumer>)`: relay multiple values or error from multi-valued reactive-sources (i.e. button-clicks) while also give flow control options right there (buffer, drop, error, etc.). + - `createSingle(Consumer>)`: relay a single value or error from other reactive sources (i.e. addListener callbacks). + - `createEmpty(Consumer)`: signal a completion or error from valueless reactive sources. + +The `Observable` will contain the following `create` methods: + + - `create(SyncGenerator)`: safe, synchronous generation of signals, one-by-one. + - `create(Consumer>)`: relay multiple values or error from multi-valued reactive-sources (i.e. button-clicks) while also give flow control options right there (buffer, drop, error, etc.). + - `createSingle(Consumer>)`: relay a single value or error from other reactive sources (i.e. addListener callbacks). + - `createEmpty(Consumer)`: signal a completion or error from valueless reactive sources. + +The `Single` will contain the following `create` method: + + - `create(Consumer>)`: relay a single value or error from other reactive sources (i.e. addListener callbacks). + +The `Completable` will contain the following `create` method: + + - `create(Consumer)`: signal a completion or error from valueless reactive sources. + + +The first two `create` methods take an implementation of an interface which provides state and the generator methods: + +```java +interface SyncGenerator { + + S createState(); + + S generate(S state, Observer output); + + void disposeState(S state); +} + +interface AsyncGenerator { + + S createState(); + + S generate(S state, long requested, Observer> output); + + void disposeState(S state); +} +``` + +These latter three `create` methods will provide the following interaction interfaces to the `java.util.function.Consumer`: + +```java +interface SingleEmitter { + + complete(T value); + + fail(Throwable error); + + stop(); + + setDisposable(Disposable d); + +} + +interface FlowEmitter { + + void next(T value); + + void fail(Throwable error); + + void complete(); + + void stop(); + + setDisposable(Disposable d); + + enum BackpressureHandling { + IGNORE, + ERROR, + DROP, + LATEST, + BUFFER + } + + void setBackpressureHandling(BackpressureHandling mode); + +} + +interface CompletableEmitter { + + complete(); + + fail(Throwable error); + + stop(); + + setDisposable(Disposable d); + +} + +``` + +By extending the base classes, operator implementations would loose the tracking/wrapping features of 1.x. To avoid this, the methods `subscribe(C)` will be final and operators have to implement a protected `subscribeActual` (or any other reasonable name). + +```java +@Override +public final void subscribe(Subscriber s) { + subscribeActual(hook.onSubscribe(s)); +} + +protected abstract void subscribeActual(Subscriber s); +``` + +Assembly-time hooks will be moved into the individual standard methods on the base types: + +```java +public final Flowable map(Function mapper) { + return hook.onAssembly(new FlowableMap(this, mapper)); +} +``` + +### Terminal behavior + +A producer can terminate a stream by emitting `onComplete` or `onError`. A consumer can terminate a stream by calling `cancel`/`dispose`. + +Any resource cleanup of the source or operators must account for any of these three termination events. In other words, if an operator needs cleanup, then it should register the cleanup callback with `cancel`/`dispose`, `onError` and `onComplete`. + +The final `subscribe` will *not* invoke `cancel`/`dispose` after receiving an `onComplete` or `onError`. + +### JVM target and source compatibility + +The 3.x version will target JDK6+ to let Android users consume the new version of RxJava. + +### Future work + +This section contains current design work which needs more discussion and elaboration before it is merged into this document as a stated goal for 3.x. + +#### Custom Observable, Single, Completable, or Flowable + +We are investigate a base interface (similar to `Publisher`) for the `Observable`, `Single`, and `Completable` (currently referred to as `Consumable` or `ConsumableObservable`). This would empower library owners and api developers to implement their own type of `Observable`, `Single`, or `Completable` without extending the class. This would result in a change the type signatures of `subscribe` as well as any operator that operates over an `Observable`, `Single`, or `Completable` to accept a more generic type (i.e. `ConsumableObservable`). For more information see the proof of concept project [Consumable](https://github.com/stealthcode/Consumable). + +#### Fusion + +Operator fusion exploits the declarative nature of building flows; the developer specifies the "what", "where" and "when", the library then tries to optimize the "how". + +There are two main levels of operator fusion: *macro* and *micro*. + +##### Macro-fusion + +Macro fusion deals with the higher level view of the operators, their identity and their combination (mostly in the form of subsequence). This is partially an internal affair of the operators, triggered by the downstream operator and may work with several cases. Given an operator application pair `a().b()` where `a` could be a source or an intermediate operator itself, when the application of `b` happens in assembly time, the following can happen: + + - `b` identifies `a` and decides to not apply itself. Example: `empty().flatMap()` is functionally a no-op. + - `b` identifies `a` and decides to apply a different, conventional operator. Example: `just().subscribeOn()` is turned into `just().observeOn()`. + - `b` decides to apply a new custom operator, combining and inlining existing behavior. Example: `just().subscribeOn()` internally goes to `ScalarScheduledPublisher`. + - `a` is `b` and the two operator's parameter set can be combined into a single application. Example: `filter(p1).filter(p2)` combined into `filter(p1 && p2)`. + +Participating in the macro-fusion externally is possible by implementing a marker interface when extending `Flowable`. Two kinds of interfaces are available: + + - `java.util.Callable`: the Java standard, throwing interface, indicating the single value has to be extracted in subscription time (or later). + - `ScalarCallable`: to indicate the single value can be safely extracted during assembly time and used/inlined in other operators: + +```java +interface ScalarCallable extends java.util.Callable { + @Override + T call(); +} +``` + +`ScalarCallable` is also `Callable` and thus its value can be extracted practically anytime. For convenience (and for sense), `ScalarCallable` overrides and hides the superclass' `throws Exception` clause - throwing during assembly time is likely unreasonable for scalars. + +Since Reactive-Streams doesn't allow `null`s in the value flow, we have the opportunity to define `ScalarCallable`s and `Callable`s returning `null` should be considered as an empty source - allowing operators to dispatch on the type `Callable` first then branch on the nullness of `call()`. + +Interoperating with other libraries, at this level is possible. Reactor-Core uses the same pattern and the two libraries can work with each other's `Publisher+Callable` types. Unfortunately, this means subscription-time only fusion as `ScalarCallable`s live locally in each library. + +##### Micro-fusion + +Micro-fusion goes a step deeper and tries to reuse internal structures, mostly queues, in operator pairs, saving on allocation and sometimes on atomic operations. It's property is that, in a way, subverts the standard Reactive-Streams protocol between subsequent operators that both support fusion. However, from the outside world's view, they still work according to the RS protocol. + +Currently, two main kinds of micro-fusion opportunities are available. + +###### 1) Conditional Subscriber + +This extends the RS `Subscriber`interface with an extra method: `boolean tryOnNext(T value)` and can help avoiding small request amounts in case an operator didn't forward but dropped the value. The canonical use is for the `filter()` operator where if the predicate returns false, the operator has to request 1 from upstream (since the downstream doesn't know there was a value dropped and thus not request itself). Operators wanting to participate in this fusion have to implement and subscribe with an extended `Subscriber` interface: + +```java +interface ConditionalSubscriber { + boolean tryOnNext(T value); +} + +//... +@Override +protected void subscribeActual(Subscriber s) { + if (s instanceof ConditionalSubscriber) { + source.subscribe(new FilterConditionalSubscriber<>(s, predicate)); + } else { + source.subscribe(new FilterRegularSubscriber<>(s, predicate)); + } +} +``` + +(Note that this may lead to extra case-implementations in operators that have some kind of queue-drain emission model.) + +###### 2) Queue-fusion + +The second category is when two (or more) operators share the same underlying queue and each append activity at the exit point (i.e. `poll()`) of the queue. This can work in two modes: synchronous and asynchronous. + +In synchronous mode, the elements of the sequence is already available (i.e. a fixed `range()` or `fromArray()`, or can be synchronously calculated in a pull fashion in `fromIterable`. In this mode, the requesting and regular onError-path is bypassed and is forbidden. Sources have to return null from `pull()` and false from `isEmpty()` if they have no more values and throw from these methods if they want to indicate an exceptional case. + +In asynchronous mode, elements may become available at any time, therefore, `pull` returning null, as with regular queue-drain, is just the indication of temporary lack of source values. Completion and error still has to go through `onComplete` and `onError` as usual, requesting still happens as usual but when a value is available in the shared queue, it is indicated by an `onNext(null)` call. This can trigger a chain of `drain` calls without moving values in or out of different queues. + +In both modes, `cancel` works and behaves as usual. + +Since this fusion mode is an optional extension, the mode switch has to be negotiated and the shared queue interface established. Operators already working with internal queues then can, mostly, keep their current `drain()` algorithm. Queue-fusion has its own interface and protocol built on top of the existing `onSubscribe`-`Subscription` rail: + +```java +interface QueueSubscription implements Queue, Subscription { + int NONE = 0; + int SYNC = 1; + int ASYNC = 2; + int ANY = SYNC | ASYNC; + int BOUNDARY = 4; + + int requestFusion(int mode); +} +``` + +For performance, the mode is an integer bitflags setup, called early during subscription time, and allows negotiating the fusion mode. Usually, producers can do only one mode and consumers can do both mode. Because fused, intermediate operators attach logic (which is many times user-callback) to the exit point of the queue interface (poll()), it may change the computation location of those callbacks in an unwanted way. The flag `BOUNDARY` is added by consumers indicating that they will consume the queue over an async boundary. Intermediate operators, such as `map` and `filter` then can reject the fusion in such sequences. + +Since RxJava 3.x is still JDK 6 compatible, the `QueueSubscription` can't itself default unnecessary methods and implementations are required to throw `UnsupportedOperationException` for `Queue` methods other than the following: + + - `poll()`. + - `isEmpty()`. + - `clear()`. + - `size()`. + +Even though other modern libraries also define this interface, they live in local packages and thus non-reusable without dragging in the whole library. Therefore, until externalized and standardized, cross-library micro-fusion won't happen. + +A consequence of the extension of the `onSubscribe`-`Subscription` rail is that intermediate operators are no longer allowed to pass an upstream `Subscription` directly to its downstream `Subscriber.onSubscribe`. Doing so is likely to have the fused sequence skip the operator completely, losing behavior or causing runtime exceptions. Since RS `Subscriber` is an interface, operators can simply implement both `Subscriber` and `Subscription` on themselves, delegating the `request` and `cancel` calls to the upstream and calling `child.onSubscribe(this)`. diff --git a/LICENSE b/LICENSE index 7f8ced0d1f..d645695673 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2012 Netflix, Inc. + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 875c7746ac..5276c0bd37 100644 --- a/README.md +++ b/README.md @@ -1,138 +1,622 @@ # RxJava: Reactive Extensions for the JVM -This library is a Java implementation of Rx Observables. + +[![codecov.io](http://codecov.io/github/ReactiveX/RxJava/coverage.svg?branch=3.x)](https://codecov.io/gh/ReactiveX/RxJava/branch/3.x) +[![Maven Central](https://maven-badges.sml.io/sonatype-central/io.reactivex.rxjava3/rxjava/badge.svg)](https://maven-badges.sml.io/sonatype-central/io.reactivex.rxjava3/rxjava) +[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod)](https://gitpod.io/#https://github.com/ReactiveX/RxJava) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/ReactiveX/RxJava/badge)](https://securityscorecards.dev/viewer/?uri=github.com/ReactiveX/RxJava) -Some of the goals of RxJava are: +RxJava is a Java VM implementation of [Reactive Extensions](http://reactivex.io): a library for composing asynchronous and event-based programs by using observable sequences. -- Stay close to the original Rx.Net implementation while adjusting naming conventions and idioms to Java -- All contracts of Rx should be the same -- Target the JVM not a language. The first languages supported (beyond Java itself) are -Groovy, -Clojure, -and Scala. -New language adapters can be contributed. -- Support Java 6+ (to include Android support) +It extends the [observer pattern](http://en.wikipedia.org/wiki/Observer_pattern) to support sequences of data/events and adds operators that allow you to compose sequences together declaratively while abstracting away concerns about things like low-level threading, synchronization, thread-safety and concurrent data structures. -Learn more about Rx on the Wiki Home and the Netflix TechBlog post where RxJava was introduced. +#### Version 3.x ([Javadoc](http://reactivex.io/RxJava/3.x/javadoc/)) -## Master Build Status +- Single dependency: [Reactive-Streams](https://github.com/reactive-streams/reactive-streams-jvm). +- Java 8+ or Android API 21+ required. +- Java 8 lambda-friendly API. +- [Android](https://github.com/ReactiveX/RxAndroid) desugar friendly. +- Fixed API mistakes and many limits of RxJava 2. +- Intended to be a replacement for RxJava 2 with relatively few binary incompatible changes. +- Non-opinionated about the source of concurrency (threads, pools, event loops, fibers, actors, etc.). +- Async or synchronous execution. +- Virtual time and schedulers for parameterized concurrency. +- Test and diagnostic support via test schedulers, test consumers and plugin hooks. +- Interop with newer JDK versions via 3rd party libraries, such as + - [Java 9 Flow API](https://github.com/akarnokd/RxJavaJdk9Interop#rxjavajdk9interop) + - [Java 21 Virtual Threads](https://github.com/akarnokd/RxJavaFiberInterop#rxjavafiberinterop) - +Learn more about RxJava in general on the Wiki Home. -## Pull Request Build Status +:information_source: Please read the [What's different in 3.0](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-3.0) for details on the changes and migration information when upgrading from 2.x. - +#### Version 2.x + +The [2.x version](https://github.com/ReactiveX/RxJava/tree/2.x) is end-of-life as of **February 28, 2021**. No further development, support, maintenance, PRs and updates will happen. The [Javadoc]([Javadoc](http://reactivex.io/RxJava/2.x/javadoc/)) of the very last version, **2.2.21**, will remain accessible. + +#### Version 1.x + +The [1.x version](https://github.com/ReactiveX/RxJava/tree/1.x) is end-of-life as of **March 31, 2018**. No further development, support, maintenance, PRs and updates will happen. The [Javadoc]([Javadoc](http://reactivex.io/RxJava/1.x/javadoc/)) of the very last version, **1.3.8**, will remain accessible. + +## Getting started + +### Setting up the dependency + +The first step is to include RxJava 3 into your project, for example, as a Gradle compile dependency: + +```groovy +implementation "io.reactivex.rxjava3:rxjava:3.x.y" +``` + +(Please replace `x` and `y` with the latest version numbers: [![Maven Central](https://maven-badges.sml.io/sonatype-central/io.reactivex.rxjava3/rxjava/badge.svg)](https://maven-badges.sml.io/sonatype-central/io.reactivex.rxjava3/rxjava) +) + +### Hello World + +The second is to write the **Hello World** program: + +```java +package rxjava.examples; + +import io.reactivex.rxjava3.core.*; + +public class HelloWorld { + public static void main(String[] args) { + Flowable.just("Hello world").subscribe(System.out::println); + } +} +``` + +Note that RxJava 3 components now live under `io.reactivex.rxjava3` and the base classes and interfaces live under `io.reactivex.rxjava3.core`. + +### Base classes + +RxJava 3 features several base classes you can discover operators on: + + - [`io.reactivex.rxjava3.core.Flowable`](http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Flowable.html): 0..N flows, supporting Reactive-Streams and backpressure + - [`io.reactivex.rxjava3.core.Observable`](http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Observable.html): 0..N flows, no backpressure, + - [`io.reactivex.rxjava3.core.Single`](http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Single.html): a flow of exactly 1 item or an error, + - [`io.reactivex.rxjava3.core.Completable`](http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Completable.html): a flow without items but only a completion or error signal, + - [`io.reactivex.rxjava3.core.Maybe`](http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Maybe.html): a flow with no items, exactly one item or an error. + +### Some terminology + +#### Upstream, downstream + +The dataflows in RxJava consist of a source, zero or more intermediate steps followed by a data consumer or combinator step (where the step is responsible to consume the dataflow by some means): + +```java +source.operator1().operator2().operator3().subscribe(consumer); + +source.flatMap(value -> source.operator1().operator2().operator3()); +``` + +Here, if we imagine ourselves on `operator2`, looking to the left towards the source is called the **upstream**. Looking to the right towards the subscriber/consumer is called the **downstream**. This is often more apparent when each element is written on a separate line: + +```java +source + .operator1() + .operator2() + .operator3() + .subscribe(consumer) +``` + +#### Objects in motion + +In RxJava's documentation, **emission**, **emits**, **item**, **event**, **signal**, **data** and **message** are considered synonyms and represent the object traveling along the dataflow. + +#### Backpressure + +When the dataflow runs through asynchronous steps, each step may perform different things with different speed. To avoid overwhelming such steps, which usually would manifest itself as increased memory usage due to temporary buffering or the need for skipping/dropping data, so-called backpressure is applied, which is a form of flow control where the steps can express how many items are they ready to process. This allows constraining the memory usage of the dataflows in situations where there is generally no way for a step to know how many items the upstream will send to it. + +In RxJava, the dedicated `Flowable` class is designated to support backpressure and `Observable` is dedicated to the non-backpressured operations (short sequences, GUI interactions, etc.). The other types, `Single`, `Maybe` and `Completable` don't support backpressure nor should they; there is always room to store one item temporarily. + +#### Assembly time + +The preparation of dataflows by applying various intermediate operators happens in the so-called **assembly time**: + +```java +Flowable flow = Flowable.range(1, 5) +.map(v -> v * v) +.filter(v -> v % 3 == 0) +; +``` + +At this point, the data is not flowing yet and no side-effects are happening. + +#### Subscription time + +This is a temporary state when `subscribe()` is called on a flow that establishes the chain of processing steps internally: + +```java +flow.subscribe(System.out::println) +```` + +This is when the **subscription side-effects** are triggered (see `doOnSubscribe`). Some sources block or start emitting items right away in this state. + +#### Runtime + +This is the state when the flows are actively emitting items, errors or completion signals: + +```java + +Observable.create(emitter -> { + while (!emitter.isDisposed()) { + long time = System.currentTimeMillis(); + emitter.onNext(time); + if (time % 2 != 0) { + emitter.onError(new IllegalStateException("Odd millisecond!")); + break; + } + } +}) +.subscribe(System.out::println, Throwable::printStackTrace); +``` + +Practically, this is when the body of the given example above executes. + +### Simple background computation + +One of the common use cases for RxJava is to run some computation, network request on a background thread and show the results (or error) on the UI thread: + +```java +import io.reactivex.rxjava3.schedulers.Schedulers; + +Flowable.fromCallable(() -> { + Thread.sleep(1000); // imitate expensive computation + return "Done"; +}) + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.single()) + .subscribe(System.out::println, Throwable::printStackTrace); + +Thread.sleep(2000); // <--- wait for the flow to finish +``` + +This style of chaining methods is called a **fluent API** which resembles the **builder pattern**. However, RxJava's reactive types are immutable; each of the method calls returns a new `Flowable` with added behavior. To illustrate, the example can be rewritten as follows: + +```java +Flowable source = Flowable.fromCallable(() -> { + Thread.sleep(1000); // imitate expensive computation + return "Done"; +}); + +Flowable runBackground = source.subscribeOn(Schedulers.io()); + +Flowable showForeground = runBackground.observeOn(Schedulers.single()); + +showForeground.subscribe(System.out::println, Throwable::printStackTrace); + +Thread.sleep(2000); +``` + +Typically, you can move computations or blocking IO to some other thread via `subscribeOn`. Once the data is ready, you can make sure they get processed on the foreground or GUI thread via `observeOn`. + +### Schedulers + +RxJava operators don't work with `Thread`s or `ExecutorService`s directly but with so-called `Scheduler`s that abstract away sources of concurrency behind a uniform API. RxJava 3 features several standard schedulers accessible via `Schedulers` utility class. + +- `Schedulers.computation()`: Run computation intensive work on a fixed number of dedicated threads in the background. Most asynchronous operators use this as their default `Scheduler`. +- `Schedulers.io()`: Run I/O-like or blocking operations on a dynamically changing set of threads. +- `Schedulers.single()`: Run work on a single thread in a sequential and FIFO manner. +- `Schedulers.trampoline()`: Run work in a sequential and FIFO manner in one of the participating threads, usually for testing purposes. + +These are available on all JVM platforms but some specific platforms, such as Android, have their own typical `Scheduler`s defined: `AndroidSchedulers.mainThread()`, `SwingScheduler.instance()` or `JavaFXScheduler.platform()`. + +In addition, there is an option to wrap an existing `Executor` (and its subtypes such as `ExecutorService`) into a `Scheduler` via `Schedulers.from(Executor)`. This can be used, for example, to have a larger but still fixed pool of threads (unlike `computation()` and `io()` respectively). + +The `Thread.sleep(2000);` at the end is no accident. In RxJava the default `Scheduler`s run on daemon threads, which means once the Java main thread exits, they all get stopped and background computations may never happen. Sleeping for some time in this example situations lets you see the output of the flow on the console with time to spare. + +### Concurrency within a flow + +Flows in RxJava are sequential in nature split into processing stages that may run **concurrently** with each other: + +```java +Flowable.range(1, 10) + .observeOn(Schedulers.computation()) + .map(v -> v * v) + .blockingSubscribe(System.out::println); +``` + +This example flow squares the numbers from 1 to 10 on the **computation** `Scheduler` and consumes the results on the "main" thread (more precisely, the caller thread of `blockingSubscribe`). However, the lambda `v -> v * v` doesn't run in parallel for this flow; it receives the values 1 to 10 on the same computation thread one after the other. + +### Parallel processing + +Processing the numbers 1 to 10 in parallel is a bit more involved: + +```java +Flowable.range(1, 10) + .flatMap(v -> + Flowable.just(v) + .subscribeOn(Schedulers.computation()) + .map(w -> w * w) + ) + .blockingSubscribe(System.out::println); +``` + +Practically, parallelism in RxJava means running independent flows and merging their results back into a single flow. The operator `flatMap` does this by first mapping each number from 1 to 10 into its own individual `Flowable`, runs them and merges the computed squares. + +Note, however, that `flatMap` doesn't guarantee any order and the items from the inner flows may end up interleaved. There are alternative operators: + + - `concatMap` that maps and runs one inner flow at a time and + - `concatMapEager` which runs all inner flows "at once" but the output flow will be in the order those inner flows were created. + +Alternatively, the `Flowable.parallel()` operator and the `ParallelFlowable` type help achieve the same parallel processing pattern: + +```java +Flowable.range(1, 10) + .parallel() + .runOn(Schedulers.computation()) + .map(v -> v * v) + .sequential() + .blockingSubscribe(System.out::println); +``` + +### Dependent sub-flows + +`flatMap` is a powerful operator and helps in a lot of situations. For example, given a service that returns a `Flowable`, we'd like to call another service with values emitted by the first service: + +```java +Flowable inventorySource = warehouse.getInventoryAsync(); + +inventorySource + .flatMap(inventoryItem -> erp.getDemandAsync(inventoryItem.getId()) + .map(demand -> "Item " + inventoryItem.getName() + " has demand " + demand)) + .subscribe(System.out::println); +``` + +### Continuations + +Sometimes, when an item has become available, one would like to perform some dependent computations on it. This is sometimes called **continuations** and, depending on what should happen and what types are involved, may involve various operators to accomplish. + +#### Dependent + +The most typical scenario is to given a value, invoke another service, await and continue with its result: + +```java +service.apiCall() +.flatMap(value -> service.anotherApiCall(value)) +.flatMap(next -> service.finalCall(next)) +``` + +It is often the case also that later sequences would require values from earlier mappings. This can be achieved by moving the outer `flatMap` into the inner parts of the previous `flatMap` for example: + +```java +service.apiCall() +.flatMap(value -> + service.anotherApiCall(value) + .flatMap(next -> service.finalCallBoth(value, next)) +) +``` + +Here, the original `value` will be available inside the inner `flatMap`, courtesy of lambda variable capture. + +#### Non-dependent + +In other scenarios, the result(s) of the first source/dataflow is irrelevant and one would like to continue with a quasi independent another source. Here, `flatMap` works as well: + +```java +Observable continued = sourceObservable.flatMapSingle(ignored -> someSingleSource) +continued.map(v -> v.toString()) + .subscribe(System.out::println, Throwable::printStackTrace); +``` + +however, the continuation in this case stays `Observable` instead of the likely more appropriate `Single`. (This is understandable because +from the perspective of `flatMapSingle`, `sourceObservable` is a multi-valued source and thus the mapping may result in multiple values as well). + +Often though there is a way that is somewhat more expressive (and also lower overhead) by using `Completable` as the mediator and its operator `andThen` to resume with something else: + +```java +sourceObservable + .ignoreElements() // returns Completable + .andThen(someSingleSource) + .map(v -> v.toString()) +``` + +The only dependency between the `sourceObservable` and the `someSingleSource` is that the former should complete normally in order for the latter to be consumed. + +#### Deferred-dependent + +Sometimes, there is an implicit data dependency between the previous sequence and the new sequence that, for some reason, was not flowing through the "regular channels". One would be inclined to write such continuations as follows: + +```java +AtomicInteger count = new AtomicInteger(); + +Observable.range(1, 10) + .doOnNext(ignored -> count.incrementAndGet()) + .ignoreElements() + .andThen(Single.just(count.get())) + .subscribe(System.out::println); +``` + +Unfortunately, this prints `0` because `Single.just(count.get())` is evaluated at **assembly time** when the dataflow hasn't even run yet. We need something that defers the evaluation of this `Single` source until **runtime** when the main source completes: + +```java +AtomicInteger count = new AtomicInteger(); + +Observable.range(1, 10) + .doOnNext(ignored -> count.incrementAndGet()) + .ignoreElements() + .andThen(Single.defer(() -> Single.just(count.get()))) + .subscribe(System.out::println); +``` + +or + +```java +AtomicInteger count = new AtomicInteger(); + +Observable.range(1, 10) + .doOnNext(ignored -> count.incrementAndGet()) + .ignoreElements() + .andThen(Single.fromCallable(() -> count.get())) + .subscribe(System.out::println); +``` + + +### Type conversions + +Sometimes, a source or service returns a different type than the flow that is supposed to work with it. For example, in the inventory example above, `getDemandAsync` could return a `Single`. If the code example is left unchanged, this will result in a compile-time error (however, often with a misleading error message about lack of overload). + +In such situations, there are usually two options to fix the transformation: 1) convert to the desired type or 2) find and use an overload of the specific operator supporting the different type. + +#### Converting to the desired type + +Each reactive base class features operators that can perform such conversions, including the protocol conversions, to match some other type. The following matrix shows the available conversion options: + +| | Flowable | Observable | Single | Maybe | Completable | +|----------|----------|------------|--------|-------|-------------| +|**Flowable** | | `toObservable` | `first`, `firstOrError`, `single`, `singleOrError`, `last`, `lastOrError`1 | `firstElement`, `singleElement`, `lastElement` | `ignoreElements` | +|**Observable**| `toFlowable`2 | | `first`, `firstOrError`, `single`, `singleOrError`, `last`, `lastOrError`1 | `firstElement`, `singleElement`, `lastElement` | `ignoreElements` | +|**Single** | `toFlowable`3 | `toObservable` | | `toMaybe` | `ignoreElement` | +|**Maybe** | `toFlowable`3 | `toObservable` | `toSingle` | | `ignoreElement` | +|**Completable** | `toFlowable` | `toObservable` | `toSingle` | `toMaybe` | | + +1: When turning a multi-valued source into a single-valued source, one should decide which of the many source values should be considered as the result. + +2: Turning an `Observable` into `Flowable` requires an additional decision: what to do with the potential unconstrained flow +of the source `Observable`? There are several strategies available (such as buffering, dropping, keeping the latest) via the `BackpressureStrategy` parameter or via standard `Flowable` operators such as `onBackpressureBuffer`, `onBackpressureDrop`, `onBackpressureLatest` which also +allow further customization of the backpressure behavior. + +3: When there is only (at most) one source item, there is no problem with backpressure as it can be always stored until the downstream is ready to consume. + + +#### Using an overload with the desired type + +Many frequently used operator has overloads that can deal with the other types. These are usually named with the suffix of the target type: + +| Operator | Overloads | +|----------|-----------| +| `flatMap` | `flatMapSingle`, `flatMapMaybe`, `flatMapCompletable`, `flatMapIterable` | +| `concatMap` | `concatMapSingle`, `concatMapMaybe`, `concatMapCompletable`, `concatMapIterable` | +| `switchMap` | `switchMapSingle`, `switchMapMaybe`, `switchMapCompletable` | + +The reason these operators have a suffix instead of simply having the same name with different signature is type erasure. Java doesn't consider signatures such as `operator(Function>)` and `operator(Function>)` different (unlike C#) and due to erasure, the two `operator`s would end up as duplicate methods with the same signature. + +### Operator naming conventions + +Naming in programming is one of the hardest things as names are expected to be not long, expressive, capturing and easily memorable. Unfortunately, the target language (and pre-existing conventions) may not give too much help in this regard (unusable keywords, type erasure, type ambiguities, etc.). + +#### Unusable keywords + +In the original Rx.NET, the operator that emits a single item and then completes is called `Return(T)`. Since the Java convention is to have a lowercase letter start a method name, this would have been `return(T)` which is a keyword in Java and thus not available. Therefore, RxJava chose to name this operator `just(T)`. The same limitation exists for the operator `Switch`, which had to be named `switchOnNext`. Yet another example is `Catch` which was named `onErrorResumeNext`. + +#### Type erasure + +Many operators that expect the user to provide some function returning a reactive type can't be overloaded because the type erasure around a `Function` turns such method signatures into duplicates. RxJava chose to name such operators by appending the type as suffix as well: + +```java +Flowable flatMap(Function> mapper) + +Flowable flatMapMaybe(Function> mapper) +``` + +#### Type ambiguities + +Even though certain operators have no problems from type erasure, their signature may turn up being ambiguous, especially if one uses Java 8 and lambdas. For example, there are several overloads of `concatWith` taking the various other reactive base types as arguments (for providing convenience and performance benefits in the underlying implementation): + +```java +Flowable concatWith(Publisher other); + +Flowable concatWith(SingleSource other); +``` + +Both `Publisher` and `SingleSource` appear as functional interfaces (types with one abstract method) and may encourage users to try to provide a lambda expression: + +```java +someSource.concatWith(s -> Single.just(2)) +.subscribe(System.out::println, Throwable::printStackTrace); +``` + +Unfortunately, this approach doesn't work and the example does not print `2` at all. In fact, since version 2.1.10, it doesn't +even compile because at least 4 `concatWith` overloads exist and the compiler finds the code above ambiguous. + +The user in such situations probably wanted to defer some computation until the `someSource` has completed, thus the correct +unambiguous operator should have been `defer`: + +```java +someSource.concatWith(Single.defer(() -> Single.just(2))) +.subscribe(System.out::println, Throwable::printStackTrace); +``` + +Sometimes, a suffix is added to avoid logical ambiguities that may compile but produce the wrong type in a flow: + +```java +Flowable merge(Publisher> sources); + +Flowable mergeArray(Publisher... sources); +``` + +This can get also ambiguous when functional interface types get involved as the type argument `T`. + +#### Error handling + +Dataflows can fail, at which point the error is emitted to the consumer(s). Sometimes though, multiple sources may fail at which point there is a choice whether or not wait for all of them to complete or fail. To indicate this opportunity, many operator names are suffixed with the `DelayError` words (while others feature a `delayError` or `delayErrors` boolean flag in one of their overloads): + +```java +Flowable concat(Publisher> sources); + +Flowable concatDelayError(Publisher> sources); +``` + +Of course, suffixes of various kinds may appear together: + +```java +Flowable concatArrayEagerDelayError(Publisher... sources); +``` + +#### Base class vs base type + +The base classes can be considered heavy due to the sheer number of static and instance methods on them. RxJava 3's design was heavily influenced by the [Reactive Streams](https://github.com/reactive-streams/reactive-streams-jvm#reactive-streams) specification, therefore, the library features a class and an interface per each reactive type: + +| Type | Class | Interface | Consumer | +|------|-------|-----------|----------| +| 0..N backpressured | `Flowable` | `Publisher`1 | `Subscriber` | +| 0..N unbounded | `Observable` | `ObservableSource`2 | `Observer` | +| 1 element or error | `Single` | `SingleSource` | `SingleObserver` | +| 0..1 element or error | `Maybe` | `MaybeSource` | `MaybeObserver` | +| 0 element or error | `Completable` | `CompletableSource` | `CompletableObserver` | + +1The `org.reactivestreams.Publisher` is part of the external Reactive Streams library. It is the main type to interact with other reactive libraries through a standardized mechanism governed by the [Reactive Streams specification](https://github.com/reactive-streams/reactive-streams-jvm#specification). + +2The naming convention of the interface was to append `Source` to the semi-traditional class name. There is no `FlowableSource` since `Publisher` is provided by the Reactive Streams library (and subtyping it wouldn't have helped with interoperation either). These interfaces are, however, not standard in the sense of the Reactive Streams specification and are currently RxJava specific only. + +### R8 and ProGuard settings + +By default, RxJava itself doesn't require any ProGuard/R8 settings and should work without problems. Unfortunately, the Reactive Streams dependency since version 1.0.3 has embedded Java 9 class files in its JAR that can cause warnings with the plain ProGuard: + +``` +Warning: org.reactivestreams.FlowAdapters$FlowPublisherFromReactive: can't find superclass or interface java.util.concurrent.Flow$Publisher +Warning: org.reactivestreams.FlowAdapters$FlowToReactiveProcessor: can't find superclass or interface java.util.concurrent.Flow$Processor +Warning: org.reactivestreams.FlowAdapters$FlowToReactiveSubscriber: can't find superclass or interface java.util.concurrent.Flow$Subscriber +Warning: org.reactivestreams.FlowAdapters$FlowToReactiveSubscription: can't find superclass or interface java.util.concurrent.Flow$Subscription +Warning: org.reactivestreams.FlowAdapters: can't find referenced class java.util.concurrent.Flow$Publisher +``` + +It is recommended one sets up the following `-dontwarn` entry in the application's `proguard-ruleset` file: + +``` +-dontwarn java.util.concurrent.Flow* +``` + +For R8, the RxJava jar includes the `META-INF/proguard/rxjava3.pro` with the same no-warning clause and should apply automatically. + +### Further reading + +For further details, consult the [wiki](https://github.com/ReactiveX/RxJava/wiki). ## Communication - Google Group: [RxJava](http://groups.google.com/d/forum/rxjava) - Twitter: [@RxJava](http://twitter.com/RxJava) -- [GitHub Issues](https://github.com/Netflix/RxJava/issues) +- [GitHub Issues](https://github.com/ReactiveX/RxJava/issues) +- StackOverflow: [rx-java](http://stackoverflow.com/questions/tagged/rx-java), [rx-java2](http://stackoverflow.com/questions/tagged/rx-java2) and [rx-java3](http://stackoverflow.com/questions/tagged/rx-java3) +- [Gitter.im](https://gitter.im/ReactiveX/RxJava) ## Versioning -RxJava is working towards a 1.0 release which will be reached once it "more or less" becomes feature complete with the [Rx.Net version](https://rx.codeplex.com). The backlog of features needed to accomplish this are documented in the [project issues](https://github.com/Netflix/RxJava/issues). +Version 3.x is in development. Bugfixes will be applied to both 2.x and 3.x branches, but new features will only be added to 3.x. -In short, once the current issue list hits 0 open we will bump to version 1.0. +Minor 3.x increments (such as 3.1, 3.2, etc) will occur when non-trivial new functionality is added or significant enhancements or bug fixes occur that may have behavioral changes that may affect some edge cases (such as dependence on behavior resulting from a bug). An example of an enhancement that would classify as this is adding reactive pull backpressure support to an operator that previously did not support it. This should be backwards compatible but does behave differently. -Until that time the "semantic versioning" will be prefixed with the 0.* and breaking changes will be done such as 0.5.x -> 0.6.x All incremental non-breaking changes with additive functionality will be done like 0.5.1 -> 0.5.2. +Patch 3.x.y increments (such as 3.0.0 -> 3.0.1, 3.3.1 -> 3.3.2, etc) will occur for bug fixes and trivial functionality (like adding a method overload). New functionality marked with an [`@Beta`][beta source link] or [`@Experimental`][experimental source link] annotation can also be added in the patch releases to allow rapid exploration and iteration of unstable new functionality. -Once we hit 1.0 it will follow the normal major.minor.patch semantic versioning approach. +#### @Beta -## Full Documentation +APIs marked with the [`@Beta`][beta source link] annotation at the class or method level are subject to change. They can be modified in any way, or even removed, at any time. If your code is a library itself (i.e. it is used on the CLASSPATH of users outside your control), you should not use beta APIs, unless you repackage them (e.g. using ProGuard, shading, etc). + +#### @Experimental + +APIs marked with the [`@Experimental`][experimental source link] annotation at the class or method level will almost certainly change. They can be modified in any way, or even removed, at any time. You should not use or rely on them in any production code. They are purely to allow broad testing and feedback. -- [Wiki](https://github.com/Netflix/RxJava/wiki) -- Javadoc +#### @Deprecated -## Code +APIs marked with the `@Deprecated` annotation at the class or method level will remain supported until the next major release, but it is recommended to stop using them. -- Java Core - - Observable - - Observer -- Groovy Adaptor -- Clojure Adaptor -- Scala Adaptor +#### io.reactivex.rxjava3.internal.* + +All code inside the `io.reactivex.rxjava3.internal.*` packages are considered private API and should not be relied upon at all. It can change at any time. + +## Full Documentation + +- [Wiki](https://github.com/ReactiveX/RxJava/wiki) +- [Javadoc](http://reactivex.io/RxJava/3.x/javadoc/) +- [Latest snaphot Javadoc](http://reactivex.io/RxJava/3.x/javadoc/snapshot/) +- Javadoc of a specific [release version](https://github.com/ReactiveX/RxJava/tags): `http://reactivex.io/RxJava/3.x/javadoc/3.x.y/` ## 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%7Cg%3A%22com.netflix.rxjava%22). +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%7Cio.reactivex.rxjava3). + +Example for Gradle: + +```groovy +implementation 'io.reactivex.rxjava3:rxjava:x.y.z' +``` -Example for Maven: +and for Maven: ```xml - com.netflix.rxjava - rxjava-core + io.reactivex.rxjava3 + rxjava x.y.z ``` and for Ivy: ```xml - + ``` -If you need to download the jars instead of using a build system, create a Maven pom file like this with the desired version: +### Snapshots -```xml - - - 4.0.0 - com.netflix.rxjava.download - rxjava-download - 1.0-SNAPSHOT - Simple POM to download rxjava-core and dependencies - http://github.com/Netflix/RxJava - - - com.netflix.rxjava - rxjava-core - x.y.z - - - - -``` +Snapshots after May 19st, 2025 are available via https://central.sonatype.com/repository/maven-snapshots/io/reactivex/rxjava3/rxjava/ -Then execute: +```groovy +repositories { + maven { url 'https://central.sonatype.com/repository/maven-snapshots' } +} +dependencies { + implementation 'io.reactivex.rxjava3:rxjava:3.0.0-SNAPSHOT' +} ``` -mvn -f download-rxjava-pom.xml dependency:copy-dependencies -``` - -It will download rxjava-core-*.jar and its dependencies into ./target/dependency/. -You need Java 6 or later. +JavaDoc snapshots are available at https://reactivex.io/RxJava/3.x/javadoc/snapshot ## Build To build: ``` -$ git clone git@github.com:Netflix/RxJava.git +$ git clone git@github.com:ReactiveX/RxJava.git $ cd RxJava/ $ ./gradlew build ``` -Futher details on building can be found on the [Getting Started](https://github.com/Netflix/RxJava/wiki/Getting-Started) page of the wiki. +Further details on building can be found on the [Getting Started](https://github.com/ReactiveX/RxJava/wiki/Getting-Started) page of the wiki. ## Bugs and Feedback -For bugs, questions and discussions please use the [Github Issues](https://github.com/Netflix/RxJava/issues). +For bugs, questions and discussions please use the [Github Issues](https://github.com/ReactiveX/RxJava/issues). ## LICENSE -Copyright 2013 Netflix, Inc. + Copyright (c) 2016-present, RxJava Contributors. + + 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 -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. -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. +[beta source link]: https://github.com/ReactiveX/RxJava/blob/3.x/src/main/java/io/reactivex/rxjava3/annotations/Beta.java +[experimental source link]: https://github.com/ReactiveX/RxJava/blob/3.x/src/main/java/io/reactivex/rxjava3/annotations/Experimental.java diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..1003574331 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. + +Please disclose it at [security advisory](https://github.com/ReactiveX/RxJava/security/advisories/new). + +This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. diff --git a/build.gradle b/build.gradle index dab55c694e..8bcfddb717 100644 --- a/build.gradle +++ b/build.gradle @@ -1,147 +1,219 @@ -ext.githubProjectName = 'RxJava' - -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') - -buildscript { - repositories { - mavenLocal() - mavenCentral() - jcenter() - } +plugins { + id("java-library") + id("checkstyle") + id("eclipse") + id("jacoco") + id("maven-publish") + id("ru.vyarus.animalsniffer") version "2.0.1" + id("me.champeau.jmh") version "0.7.3" + id("com.github.hierynomus.license") version "0.16.1" + id("biz.aQute.bnd.builder") version "6.4.0" + id("com.vanniktech.maven.publish") version "0.33.0" + id("org.beryx.jar") version "2.0.0" + id("signing") +} - apply from: file('gradle/buildscript.gradle'), to: buildscript +ext { + reactiveStreamsVersion = "1.0.4" + junitVersion = "4.13.2" + testNgVersion = "7.5" + mockitoVersion = "4.11.0" + jmhLibVersion = "1.21" + guavaVersion = "33.5.0-jre" } -allprojects { - apply plugin: 'eclipse' - apply plugin: 'idea' - repositories { - mavenLocal() - mavenCentral() +def releaseTag = System.getenv("BUILD_TAG") +if (releaseTag != null && !releaseTag.isEmpty()) { + if (releaseTag.startsWith("v")) { + releaseTag = releaseTag.substring(1) } + project.version = releaseTag + + logger.lifecycle("Releasing with version: " + project.version) } -subprojects { - apply plugin: 'java' - apply plugin: 'shadow' - group = "com.netflix.rxjava" +repositories { + mavenCentral() +} - // everything defaults to 1.6 - sourceCompatibility = JavaVersion.VERSION_1_6 - targetCompatibility = JavaVersion.VERSION_1_6 +dependencies { + signature "org.codehaus.mojo.signature:java18:1.0@signature" - // make 'examples' use the same classpath - configurations { - examplesCompile.extendsFrom compile - examplesRuntime.extendsFrom runtime - perfCompile.extendsFrom compile - perfRuntime.extendsFrom runtime - } + api "org.reactivestreams:reactive-streams:$reactiveStreamsVersion" + jmh "org.reactivestreams:reactive-streams:$reactiveStreamsVersion" + testImplementation "junit:junit:$junitVersion" + testImplementation "org.mockito:mockito-core:$mockitoVersion" - tasks.withType(Javadoc).each { - it.classpath = sourceSets.main.compileClasspath - } + testImplementation "org.reactivestreams:reactive-streams-tck:$reactiveStreamsVersion" + testImplementation "org.testng:testng:$testNgVersion" + testImplementation "com.google.guava:guava:$guavaVersion" +} - sourceSets { - examples - perf { - compileClasspath += sourceSets.main.output +def buildWith11 = System.getenv("BUILD_WITH_11") +java { + toolchain { + vendor = JvmVendorSpec.ADOPTIUM + if ("true".equals(buildWith11)) { + languageVersion = JavaLanguageVersion.of(11) + } else { + languageVersion = JavaLanguageVersion.of(8) } } + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} - tasks.build { - //include 'examples' in build task - dependsOn(examplesClasses) - dependsOn(perfClasses) - } +tasks.withType(JavaCompile) { + options.compilerArgs << "-parameters" +} - task perfJar(type: Jar, dependsOn: perfClasses) { - from sourceSets.perf.output + sourceSets.main.output - } +apply from: file("gradle/javadoc_cleanup.gradle") - dependencies { - perfCompile 'org.openjdk.jmh:jmh-core:0.9' - perfCompile 'org.openjdk.jmh:jmh-generator-annprocess:0.9' +javadoc { + exclude "**/internal/**" + exclude "**/test/**" + exclude "**/perf/**" + exclude "**/jmh/**" + options { + windowTitle = "RxJava Javadoc ${project.version}" } + // Clear the following options to make the docs consistent with the old format + options.addStringOption("top").value = "" + options.addStringOption("doctitle").value = "" + options.addStringOption("header").value = "" + options.stylesheetFile = project.file("gradle/stylesheet.css") + + options.links( + "https://docs.oracle.com/javase/8/docs/api/", + "https://reactivex.io/RxJava/org.reactivestreams.javadoc/${reactiveStreamsVersion}/" + ) + + finalizedBy javadocCleanup +} + +animalsniffer { + annotation = "io.reactivex.rxjava3.internal.util.SuppressAnimalSniffer" +} + +moduleConfig { + moduleInfoPath = 'src/main/module/module-info.java' + multiReleaseVersion = 9 + version = project.version +} - artifacts { - perfRuntime perfJar +jar { + from('.') { + include 'LICENSE' + include 'COPYRIGHT' + into('META-INF/') } - - eclipse { - classpath { - plusConfigurations += configurations.perfCompile - - downloadSources = true - downloadJavadoc = true - } + exclude("module-info.class") + + // Cover for bnd still not supporting MR Jars: https://github.com/bndtools/bnd/issues/2227 + bnd('-fixupmessages': '^Classes found in the wrong directory: \\\\{META-INF/versions/9/module-info\\\\.class=module-info}$') + bnd( + "Bundle-Name": "rxjava", + "Bundle-Vendor": "RxJava Contributors", + "Bundle-Description": "Reactive Extensions for the JVM - a library for composing asynchronous and event-based programs using observable sequences for the Java VM.", + "Import-Package": "!org.junit,!junit.framework,!org.mockito.*,!org.testng.*,*", + "Bundle-DocURL": "https://github.com/ReactiveX/RxJava", + "Eclipse-ExtensibleAPI": "true", + "Export-Package": "!io.reactivex.rxjava3.internal.*, io.reactivex.rxjava3.*", + "Bundle-SymbolicName": "io.reactivex.rxjava3.rxjava", + "Multi-Release": "true" + ) +} + +license { + header project.file("config/license/HEADER") + ext.year = Calendar.getInstance().get(Calendar.YEAR) + skipExistingHeaders true + ignoreFailures true + excludes(["**/*.md", "**/*.txt"]) +} + +jmh { + jmhVersion = jmhLibVersion + humanOutputFile = null + includeTests = false + jvmArgs = ["-Djmh.ignoreLock=true"] + jvmArgsAppend = ["-Djmh.separateClasspathJAR=true"] + + if (project.hasProperty("jmh")) { + includes = [".*" + project.jmh + ".*"] + logger.info("JMH: {}", includes) } +} - idea { - module { - scopes.PROVIDED.plus += configurations.perfCompile - scopes.PROVIDED.minus += configurations.compile - } +test { + maxHeapSize = "1200m" +} + +task testNG(type: Test) { + useTestNG() +} + +check.dependsOn testNG + +tasks.withType(Test) { + testLogging { + events = ["skipped", "failed"] + exceptionFormat = "full" + + debug.events = ["skipped", "failed"] + debug.exceptionFormat = "full" + + info.events = ["failed", "skipped"] + info.exceptionFormat = "full" + + warn.events = ["failed", "skipped"] + warn.exceptionFormat = "full" } - /** - * By default: Run without arguments this will execute all benchmarks that are found (can take a long time). - * - * Optionally pass arguments for custom execution. Example: - * - * ../gradlew benchmarks '-Pjmh=-f 1 -tu ns -bm avgt -wi 5 -i 5 -r 1 .*OperatorSerializePerf.*' - * - * To see all options: - * - * ../gradlew benchmarks '-Pjmh=-h' - */ - task benchmarks(type: JavaExec) { - main = 'org.openjdk.jmh.Main' - classpath = sourceSets.perf.runtimeClasspath + sourceSets.main.output - maxHeapSize = "512m" - jvmArgs '-XX:+UnlockCommercialFeatures' - jvmArgs '-XX:+FlightRecorder' - jvmArgs '-XX:AutoBoxCacheMax=1000000' - - if (project.hasProperty('jmh')) { - args(jmh.split(' ')) - } else { - //args '-h' // help output - args '-f' // fork - args '1' - args '-wi' // warmup iterations - args '5' - args '-i' // test iterations - args '5' - args '-r' // time per execution in seconds - args '5' - //args '-prof' // profilers - //args 'HS_GC' // HotSpot (tm) memory manager (GC) profiling via implementation-specific MBeans - //args 'HS_RT' // HotSpot (tm) runtime profiling via implementation-specific MBeans - //args 'HS_THR' // HotSpot (tm) threading subsystem via implementation-specific MBeans - //args 'HS_COMP' // HotSpot (tm) JIT compiler profiling via implementation-specific MBeans - //args 'HS_CL' // HotSpot (tm) classloader profiling via implementation-specific MBeans - //args 'STACK' // Simple and naive Java stack profiler - } + if (System.getenv("CI") == null) { + maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 } +} - shadow { - classifier = "benchmarks" - includeDependenciesFor = ["runtime", "perfRuntime"] +jacocoTestReport { + dependsOn test + dependsOn testNG - transformer(com.github.jengelman.gradle.plugins.shadow.transformers.ManifestResourceTransformer) { - mainClass = "org.openjdk.jmh.Main" - } + reports { + xml.required.set(true) + csv.required.set(false) + html.required.set(true) } +} - shadowJar.dependsOn perfJar +check.dependsOn jacocoTestReport + +checkstyle { + configFile = project.file("config/checkstyle/checkstyle.xml") + configProperties = [ + "checkstyle.suppressions.file": project.file("config/checkstyle/suppressions.xml"), + "checkstyle.header.file" : project.file("config/license/HEADER_JAVA") + ] + checkstyleMain.exclude '**/module-info.java' } -project(':rxjava-core') { - sourceSets.test.java.srcDir 'src/test/java' +if (project.hasProperty("releaseMode")) { + logger.lifecycle("ReleaseMode: {}", project.releaseMode) + + + if ("full" == project.releaseMode) { + signing { + if (project.hasProperty("SIGNING_PRIVATE_KEY") && project.hasProperty("SIGNING_PASSWORD")) { + useInMemoryPgpKeys(project.getProperty("SIGNING_PRIVATE_KEY"), project.getProperty("SIGNING_PASSWORD")) + sign(publishing.publications) + } + } + } + mavenPublishing { + // or when publishing to https://central.sonatype.com/ + publishToMavenCentral(com.vanniktech.maven.publish.SonatypeHost.CENTRAL_PORTAL) + + // signAllPublications() + } } diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000..174689e7ec --- /dev/null +++ b/codecov.yml @@ -0,0 +1,13 @@ +coverage: + status: + project: + default: + target: 95% + threshold: 1% + + patch: + default: + target: 95% + threshold: 1% + + changes: no diff --git a/codequality/HEADER b/codequality/HEADER deleted file mode 100644 index 3102e4b449..0000000000 --- a/codequality/HEADER +++ /dev/null @@ -1,13 +0,0 @@ -Copyright ${year} 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. diff --git a/codequality/checkstyle.xml b/codequality/checkstyle.xml deleted file mode 100644 index 47c01a2ea1..0000000000 --- a/codequality/checkstyle.xml +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..05896aee12 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 0000000000..cf580e45e6 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/config/license/HEADER b/config/license/HEADER new file mode 100644 index 0000000000..3949e0b453 --- /dev/null +++ b/config/license/HEADER @@ -0,0 +1,10 @@ +Copyright (c) 2016-present, RxJava Contributors. + +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. diff --git a/config/license/HEADER_JAVA b/config/license/HEADER_JAVA new file mode 100644 index 0000000000..d95b44938b --- /dev/null +++ b/config/license/HEADER_JAVA @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2016-present, RxJava Contributors. + * + * 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. + */ diff --git a/docs/Additional-Reading.md b/docs/Additional-Reading.md new file mode 100644 index 0000000000..4badd81308 --- /dev/null +++ b/docs/Additional-Reading.md @@ -0,0 +1,63 @@ +A more complete and up-to-date list of resources can be found at the [reactivex.io site](http://reactivex.io/tutorials.html) + +# Introducing Reactive Programming +* [Introduction to Rx](http://www.introtorx.com/): a free, on-line book by Lee Campbell **(1.x)** +* [The introduction to Reactive Programming you've been missing](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) by Andre Staltz +* [Mastering Observables](https://docs.huihoo.com/couchbase/developer-guide/java-2.0/observables.html) from the Couchbase documentation **(1.x)** +* [Reactive Programming in Java 8 With RxJava](http://pluralsight.com/training/Courses/TableOfContents/reactive-programming-java-8-rxjava), a course designed by Russell Elledge **(1.x)** +* [33rd Degree Reactive Java](http://www.slideshare.net/tkowalcz/33rd-degree-reactive-java) by Tomasz Kowalczewski **(1.x)** +* [What Every Hipster Should Know About Functional Reactive Programming](http://www.infoq.com/presentations/game-functional-reactive-programming) - Bodil Stokke demos the creation of interactive game mechanics in RxJS +* [Your Mouse is a Database](http://queue.acm.org/detail.cfm?id=2169076) by Erik Meijer +* [A Playful Introduction to Rx](https://www.youtube.com/watch?v=WKore-AkisY) a video lecture by Erik Meijer +* Wikipedia: [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) and [Functional Reactive Programming](http://en.wikipedia.org/wiki/Functional_reactive_programming) +* [What is Reactive Programming?](https://www.youtube.com/watch?v=-8Y1-lE6NSA) a video presentation by Jafar Husain. +* [2 minute introduction to Rx](https://medium.com/@andrestaltz/2-minute-introduction-to-rx-24c8ca793877) by André Staltz +* StackOverflow: [What is (functional) reactive programming?](http://stackoverflow.com/a/1030631/1946802) +* [The Reactive Manifesto](http://www.reactivemanifesto.org/) +* Grokking RxJava, [Part 1: The Basics](http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/), [Part 2: Operator, Operator](http://blog.danlew.net/2014/09/22/grokking-rxjava-part-2/), [Part 3: Reactive with Benefits](http://blog.danlew.net/2014/09/30/grokking-rxjava-part-3/), [Part 4: Reactive Android](http://blog.danlew.net/2014/10/08/grokking-rxjava-part-4/) - published in Sep/Oct 2014 by Daniel Lew **(1.x)** +* [Reactive Programming on Android with RxJava](https://leanpub.com/reactiveandroid), a free e-book by Chris Arriola and Angus Huang. + +# How Netflix Is Using RxJava +* LambdaJam Chicago 2013: [Functional Reactive Programming in the Netflix API](https://speakerdeck.com/benjchristensen/functional-reactive-programming-in-the-netflix-api-lambdajam-2013) by Ben Christensen **(1.x)** +* QCon London 2013 presentation: [Functional Reactive Programming in the Netflix API](http://www.infoq.com/presentations/netflix-functional-rx) and a related [interview](http://www.infoq.com/interviews/christensen-hystrix-rxjava) with Ben Christensen **(1.x)** +* [Functional Reactive in the Netflix API with RxJava](http://techblog.netflix.com/2013/02/rxjava-netflix-api.html) by Ben Christensen and Jafar Husain **(1.x)** +* [Optimizing the Netflix API](http://techblog.netflix.com/2013/01/optimizing-netflix-api.html) by Ben Christensen **(1.x)** +* [Reactive Programming at Netflix](http://techblog.netflix.com/2013/01/reactive-programming-at-netflix.html) by Jafar Husain **(1.x)** + +# RxScala +* [RxJava: Reactive Extensions in Scala](http://www.youtube.com/watch?v=tOMK_FYJREw&feature=youtu.be): video of Ben Christensen and Matt Jacobs presenting at SF Scala **(1.x)** + +# Rx.NET +* [rx.codeplex.com](https://rx.codeplex.com) +* [Rx Design Guidelines (PDF)](http://go.microsoft.com/fwlink/?LinkID=205219) +* [Channel 9 MSDN videos on Reactive Extensions](http://channel9.msdn.com/Tags/reactive+extensions) +* [Beginner’s Guide to the Reactive Extensions](http://msdn.microsoft.com/en-us/data/gg577611) +* [Rx Is now Open Source](http://www.hanselman.com/blog/ReactiveExtensionsRxIsNowOpenSource.aspx) by Scott Hanselman +* [Rx Workshop: Observables vs. Events](http://channel9.msdn.com/Series/Rx-Workshop/Rx-Workshop-Observables-versus-Events) +* [Rx Workshop: Unified Programming Model](http://channel9.msdn.com/Series/Rx-Workshop/Rx-Workshop-Unified-Programming-Model) +* [MSDN Rx forum](http://social.msdn.microsoft.com/Forums/en-US/home?forum=rx) + +# RxJS +* [the RxJS github site](https://github.com/reactivex/rxjs) +* An interactive tutorial: [Functional Programming in Javascript](http://jhusain.github.io/learnrx/) and [an accompanying lecture (video)](http://www.youtube.com/watch?v=LB4lhFJBBq0) by Jafar Husain +* [Netflix JavaScript Talks - Async JavaScript with Reactive Extensions](https://www.youtube.com/watch?v=XRYN2xt11Ek) video of a talk by Jafar Husain about the Rx way of programming +* [RxJS](https://xgrommx.github.io/rx-book/), an on-line book by @xgrommx +* [Journey from procedural to reactive Javascript with stops](https://glebbahmutov.com/blog/journey-from-procedural-to-reactive-javascript-with-stops/) by Gleb Bahmutov + +# RxAndroid + +* [FRP on Android](http://slides.com/yaroslavheriatovych/frponandroid#/) - publish in Jan 2014 by Yaroslav Heriatovych **(1.x)** +* [RxAndroid Github page](https://github.com/ReactiveX/RxAndroid) **(2.x)** +* [RxAndroid basics](https://medium.com/@kurtisnusbaum/rxandroid-basics-part-1-c0d5edcf6850) **(1.x & 2.x)** +* [RxJava and RxAndroid on AndroidHive](https://www.androidhive.info/RxJava/) **(1.x & 2.x)** +* [Reactive Programming with RxAndroid in Kotlin: An Introduction](https://www.raywenderlich.com/384-reactive-programming-with-rxandroid-in-kotlin-an-introduction) **(2.x)** +* [Difference between RxJava and RxAndroid](https://stackoverflow.com/questions/49651249/difference-between-rxjava-and-rxandroid) **(2.x)** +* [Reactive programming with RxAndroid](https://www.androidauthority.com/reactive-programming-with-rxandroid-711104/) **(1.x)** +* [RxJava - Vogella.com](http://www.vogella.com/tutorials/RxJava/article.html) **(2.x)** +* [Funcitional reactive Android](https://www.toptal.com/android/functional-reactive-android-rxjava) **(1.x)** +* [Reactive Programming with RxAndroid and Kotlin](https://www.pluralsight.com/courses/rxandroid-kotlin-reactive-programming) + +# Miscellany +* [RxJava Observables and Akka Actors](http://onoffswitch.net/rxjava-observables-akka-actors/) by Anton Kropp **(1.x & 2.x)** +* [Vert.x and RxJava](http://slid.es/petermd/eclipsecon2014) by @petermd **(1.x)** +* [RxJava in Different Flavours of Java](http://instil.co/2014/08/05/rxjava-in-different-flavours-of-java/): Java 7 and Java 8 implementations of the same code **(1.x)** diff --git a/docs/Alphabetical-List-of-Observable-Operators.md b/docs/Alphabetical-List-of-Observable-Operators.md new file mode 100644 index 0000000000..e5728356bc --- /dev/null +++ b/docs/Alphabetical-List-of-Observable-Operators.md @@ -0,0 +1,250 @@ +* **`aggregate( )`** — _see [**`reduce( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#reduce)_ +* [**`all( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#boolean-operators) — determine whether all items emitted by an Observable meet some criteria +* [**`amb( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators) — given two or more source Observables, emits all of the items from the first of these Observables to emit an item +* **`ambWith( )`** — _instance version of [**`amb( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators)_ +* [**`and( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#and-then-and-when) — combine the emissions from two or more source Observables into a `Pattern` (`rxjava-joins`) +* **`apply( )`** (scala) — _see [**`create( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#create)_ +* **`asObservable( )`** (kotlin) — _see [**`from( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#from) (et al.)_ +* [**`asyncAction( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators#toasync-or-asyncaction-or-asyncfunc) — convert an Action into an Observable that executes the Action and emits its return value (`rxjava-async`) +* [**`asyncFunc( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators#toasync-or-asyncaction-or-asyncfunc) — convert a function into an Observable that executes the function and emits its return value (`rxjava-async`) +* [**`averageDouble( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#averagedouble) — calculates the average of Doubles emitted by an Observable and emits this average (`rxjava-math`) +* [**`averageFloat( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#averagefloat) — calculates the average of Floats emitted by an Observable and emits this average (`rxjava-math`) +* [**`averageInteger( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators) — calculates the average of Integers emitted by an Observable and emits this average (`rxjava-math`) +* [**`averageLong( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators) — calculates the average of Longs emitted by an Observable and emits this average (`rxjava-math`) +* **`blocking( )`** (clojure) — _see [**`toBlocking( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators)_ +* [**`buffer( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#buffer) — periodically gather items from an Observable into bundles and emit these bundles rather than emitting the items one at a time +* [**`byLine( )`**](https://github.com/ReactiveX/RxJava/wiki/String-Observables) (`StringObservable`) — converts an Observable of Strings into an Observable of Lines by treating the source sequence as a stream and splitting it on line-endings +* [**`cache( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — remember the sequence of items emitted by the Observable and emit the same sequence to future Subscribers +* [**`cast( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#cast) — cast all items from the source Observable into a particular type before reemitting them +* **`catch( )`** (clojure) — _see [**`onErrorResumeNext( )`**](https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators#onerrorresumenext)_ +* [**`chunkify( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#chunkify) — returns an iterable that periodically returns a list of items emitted by the source Observable since the last list (⁇) +* [**`collect( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#collect) — collects items emitted by the source Observable into a single mutable data structure and returns an Observable that emits this structure +* [**`combineLatest( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#combinelatest) — when an item is emitted by either of two Observables, combine the latest item emitted by each Observable via a specified function and emit items based on the results of this function +* **`combineLatestWith( )`** (scala) — _instance version of [**`combineLatest( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#combinelatest)_ +* [**`concat( )`**](http://reactivex.io/documentation/operators/concat.html) — concatenate two or more Observables sequentially +* [**`concatMap( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#concatmap) — transform the items emitted by an Observable into Observables, then flatten this into a single Observable, without interleaving +* **`concatWith( )`** — _instance version of [**`concat( )`**](http://reactivex.io/documentation/operators/concat.html)_ +* [**`connect( )`**](https://github.com/ReactiveX/RxJava/wiki/Connectable-Observable-Operators) — instructs a Connectable Observable to begin emitting items +* **`cons( )`** (clojure) — _see [**`concat( )`**](http://reactivex.io/documentation/operators/concat.html)_ +* [**`contains( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#boolean-operators) — determine whether an Observable emits a particular item or not +* [**`count( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#count) — counts the number of items emitted by an Observable and emits this count +* [**`countLong( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#count) — counts the number of items emitted by an Observable and emits this count +* [**`create( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#create) — create an Observable from scratch by means of a function +* **`cycle( )`** (clojure) — _see [**`repeat( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables)_ +* [**`debounce( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#debounce) — only emit an item from the source Observable after a particular timespan has passed without the Observable emitting any other items +* [**`decode( )`**](https://github.com/ReactiveX/RxJava/wiki/String-Observables) (`StringObservable`) — convert a stream of multibyte characters into an Observable that emits byte arrays that respect character boundaries +* [**`defaultIfEmpty( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators) — emit items from the source Observable, or emit a default item if the source Observable completes after emitting no items +* [**`defer( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#defer) — do not create the Observable until a Subscriber subscribes; create a fresh Observable on each subscription +* [**`deferFuture( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) — convert a Future that returns an Observable into an Observable, but do not attempt to get the Observable that the Future returns until a Subscriber subscribes (`rxjava-async`) +* [**`deferCancellableFuture( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#fromcancellablefuture-startcancellablefuture-and-defercancellablefuture) — convert a Future that returns an Observable into an Observable in a way that monitors the subscription status of the Observable to determine whether to halt work on the Future, but do not attempt to get the returned Observable until a Subscriber subscribes (⁇)(`rxjava-async`) +* [**`delay( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — shift the emissions from an Observable forward in time by a specified amount +* [**`dematerialize( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — convert a materialized Observable back into its non-materialized form +* [**`distinct( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#distinct) — suppress duplicate items emitted by the source Observable +* [**`distinctUntilChanged( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#distinctuntilchanged) — suppress duplicate consecutive items emitted by the source Observable +* **`do( )`** (clojure) — _see [**`doOnEach( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators)_ +* [**`doOnCompleted( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — register an action to take when an Observable completes successfully +* [**`doOnEach( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — register an action to take whenever an Observable emits an item +* [**`doOnError( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — register an action to take when an Observable completes with an error +* **`doOnNext( )`** — _see [**`doOnEach( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators)_ +* **`doOnRequest( )`** — register an action to take when items are requested from an Observable via reactive-pull backpressure (⁇) +* [**`doOnSubscribe( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — register an action to take when an observer subscribes to an Observable +* [**`doOnTerminate( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — register an action to take when an Observable completes, either successfully or with an error +* [**`doOnUnsubscribe( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — register an action to take when an observer unsubscribes from an Observable +* [**`doWhile( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators) — emit the source Observable's sequence, and then repeat the sequence as long as a condition remains true (`contrib-computation-expressions`) +* **`drop( )`** (scala/clojure) — _see [**`skip( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#skip)_ +* **`dropRight( )`** (scala) — _see [**`skipLast( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#skiplast)_ +* **`dropUntil( )`** (scala) — _see [**`skipUntil( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators)_ +* **`dropWhile( )`** (scala) — _see [**`skipWhile( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators)_ +* **`drop-while( )`** (clojure) — _see [**`skipWhile( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#skipwhile)_ +* [**`elementAt( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#elementat) — emit item _n_ emitted by the source Observable +* [**`elementAtOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) — emit item _n_ emitted by the source Observable, or a default item if the source Observable emits fewer than _n_ items +* [**`empty( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#empty) — create an Observable that emits nothing and then completes +* [**`encode( )`**](https://github.com/ReactiveX/RxJava/wiki/String-Observables) (`StringObservable`) — transform an Observable that emits strings into an Observable that emits byte arrays that respect character boundaries of multibyte characters in the original strings +* [**`error( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#error) — create an Observable that emits nothing and then signals an error +* **`every( )`** (clojure) — _see [**`all( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#boolean-operators)_ +* [**`exists( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#boolean-operators) — determine whether an Observable emits any items or not +* [**`filter( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#filter) — filter items emitted by an Observable +* **`finally( )`** (clojure) — _see [**`finallyDo( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators)_ +* **`filterNot( )`** (scala) — _see [**`filter( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#filter)_ +* [**`finallyDo( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — register an action to take when an Observable completes +* [**`first( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#first) (`Observable`) — emit only the first item emitted by an Observable, or the first item that meets some condition +* [**`first( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`) — emit only the first item emitted by an Observable, or the first item that meets some condition +* [**`firstOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) (`Observable`) — emit only the first item emitted by an Observable, or the first item that meets some condition, or a default value if the source Observable is empty +* [**`firstOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`) — emit only the first item emitted by an Observable, or the first item that meets some condition, or a default value if the source Observable is empty +* **`firstOrElse( )`** (scala) — _see [**`firstOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) or [**`firstOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`)_ +* [**`flatMap( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#flatmap) — transform the items emitted by an Observable into Observables, then flatten this into a single Observable +* [**`flatMapIterable( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#flatmapiterable) — create Iterables corresponding to each emission from a source Observable and merge the results into a single Observable +* **`flatMapIterableWith( )`** (scala) — _instance version of [**`flatMapIterable( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#flatmapiterable)_ +* **`flatMapWith( )`** (scala) — _instance version of [**`flatmap( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#flatmap)_ +* **`flatten( )`** (scala) — _see [**`merge( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#merge)_ +* **`flattenDelayError( )`** (scala) — _see [**`mergeDelayError( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#mergedelayerror)_ +* **`foldLeft( )`** (scala) — _see [**`reduce( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#reduce)_ +* **`forall( )`** (scala) — _see [**`all( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#boolean-operators)_ +* **`forEach( )`** (`Observable`) — _see [**`subscribe( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable)_ +* [**`forEach( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`) — invoke a function on each item emitted by the Observable; block until the Observable completes +* [**`forEachFuture( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) (`Async`) — pass Subscriber methods to an Observable but also have it behave like a Future that blocks until it completes (`rxjava-async`) +* [**`forEachFuture( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#foreachfuture) (`BlockingObservable`)— create a futureTask that will invoke a specified function on each item emitted by an Observable (⁇) +* [**`forIterable( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#foriterable) — apply a function to the elements of an Iterable to create Observables which are then concatenated (⁇) +* [**`from( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#from) — convert an Iterable, a Future, or an Array into an Observable +* [**`from( )`**](https://github.com/ReactiveX/RxJava/wiki/String-Observables) (`StringObservable`) — convert a stream of characters or a Reader into an Observable that emits byte arrays or Strings +* [**`fromAction( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) — convert an Action into an Observable that invokes the action and emits its result when a Subscriber subscribes (`rxjava-async`) +* [**`fromCallable( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) — convert a Callable into an Observable that invokes the callable and emits its result or exception when a Subscriber subscribes (`rxjava-async`) +* [**`fromCancellableFuture( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#fromcancellablefuture-startcancellablefuture-and-defercancellablefuture) — convert a Future into an Observable in a way that monitors the subscription status of the Observable to determine whether to halt work on the Future, but do not attempt to get the Future's value until a Subscriber subscribes (⁇)(`rxjava-async`) +* **`fromFunc0( )`** — _see [**`fromCallable( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) (`rxjava-async`)_ +* [**`fromFuture( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#fromfuture) — convert a Future into an Observable, but do not attempt to get the Future's value until a Subscriber subscribes (⁇) +* [**`fromRunnable( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators#fromrunnable) — convert a Runnable into an Observable that invokes the runable and emits its result when a Subscriber subscribes (`rxjava-async`) +* [**`generate( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#generate-and-generateabsolutetime) — create an Observable that emits a sequence of items as generated by a function of your choosing (⁇) +* [**`generateAbsoluteTime( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#generate-and-generateabsolutetime) — create an Observable that emits a sequence of items as generated by a function of your choosing, with each item emitted at an item-specific time (⁇) +* **`generator( )`** (clojure) — _see [**`generate( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#generate-and-generateabsolutetime)_ +* [**`getIterator( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) — convert the sequence emitted by the Observable into an Iterator +* [**`groupBy( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#groupby) — divide an Observable into a set of Observables that emit groups of items from the original Observable, organized by key +* **`group-by( )`** (clojure) — _see [**`groupBy( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#groupby)_ +* [**`groupByUntil( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators) — a variant of the [`groupBy( )`](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#groupby) operator that closes any open GroupedObservable upon a signal from another Observable (⁇) +* [**`groupJoin( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#joins) — combine the items emitted by two Observables whenever one item from one Observable falls within a window of duration specified by an item emitted by the other Observable +* **`head( )`** (scala) — _see [**`first( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`)_ +* **`headOption( )`** (scala) — _see [**`firstOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) or [**`firstOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`)_ +* **`headOrElse( )`** (scala) — _see [**`firstOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) or [**`firstOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`)_ +* [**`ifThen( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators) — only emit the source Observable's sequence if a condition is true, otherwise emit an empty or default sequence (`contrib-computation-expressions`) +* [**`ignoreElements( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#ignoreelements) — discard the items emitted by the source Observable and only pass through the error or completed notification +* [**`interval( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#interval) — create an Observable that emits a sequence of integers spaced by a given time interval +* **`into( )`** (clojure) — _see [**`reduce( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#reduce)_ +* [**`isEmpty( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#boolean-operators) — determine whether an Observable emits any items or not +* **`items( )`** (scala) — _see [**`just( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#just)_ +* [**`join( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#joins) — combine the items emitted by two Observables whenever one item from one Observable falls within a window of duration specified by an item emitted by the other Observable +* [**`join( )`**](https://github.com/ReactiveX/RxJava/wiki/String-Observables) (`StringObservable`) — converts an Observable that emits a sequence of strings into an Observable that emits a single string that concatenates them all, separating them by a specified string +* [**`just( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#just) — convert an object into an Observable that emits that object +* [**`last( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`) — block until the Observable completes, then return the last item emitted by the Observable +* [**`last( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#last) (`Observable`) — emit only the last item emitted by the source Observable +* **`lastOption( )`** (scala) — _see [**`lastOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) or [**`lastOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`)_ +* [**`lastOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`) — block until the Observable completes, then return the last item emitted by the Observable or a default item if there is no last item +* [**`lastOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) (`Observable`) — emit only the last item emitted by an Observable, or a default value if the source Observable is empty +* **`lastOrElse( )`** (scala) — _see [**`lastOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) or [**`lastOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`)_ +* [**`latest( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) — returns an iterable that blocks until or unless the Observable emits an item that has not been returned by the iterable, then returns the latest such item +* **`length( )`** (scala) — _see [**`count( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#count)_ +* **`limit( )`** — _see [**`take( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#take)_ +* **`longCount( )`** (scala) — _see [**`countLong( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators)_ +* [**`map( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#map) — transform the items emitted by an Observable by applying a function to each of them +* **`mapcat( )`** (clojure) — _see [**`concatMap( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#concatmap)_ +* **`mapMany( )`** — _see: [**`flatMap( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#flatmap)_ +* [**`materialize( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — convert an Observable into a list of Notifications +* [**`max( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#max) — emits the maximum value emitted by a source Observable (`rxjava-math`) +* [**`maxBy( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators) — emits the item emitted by the source Observable that has the maximum key value (`rxjava-math`) +* [**`merge( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#merge) — combine multiple Observables into one +* [**`mergeDelayError( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#mergedelayerror) — combine multiple Observables into one, allowing error-free Observables to continue before propagating errors +* **`merge-delay-error( )`** (clojure) — _see [**`mergeDelayError( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#mergedelayerror)_ +* **`mergeMap( )`** * — _see: [**`flatMap( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#flatmap)_ +* **`mergeMapIterable( )`** — _see: [**`flatMapIterable( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#flatmapiterable)_ +* **`mergeWith( )`** — _instance version of [**`merge( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#merge)_ +* [**`min( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#min) — emits the minimum value emitted by a source Observable (`rxjava-math`) +* [**`minBy( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators) — emits the item emitted by the source Observable that has the minimum key value (`rxjava-math`) +* [**`mostRecent( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) — returns an iterable that always returns the item most recently emitted by the Observable +* [**`multicast( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#multicast) — represents an Observable as a Connectable Observable +* [**`never( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#never) — create an Observable that emits nothing at all +* [**`next( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) — returns an iterable that blocks until the Observable emits another item, then returns that item +* **`nonEmpty( )`** (scala) — _see [**`isEmpty( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#boolean-operators)_ +* **`nth( )`** (clojure) — _see [**`elementAt( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#elementat) and [**`elementAtOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables)_ +* [**`observeOn( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — specify on which Scheduler a Subscriber should observe the Observable +* [**`ofType( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#oftype) — emit only those items from the source Observable that are of a particular class +* [**`onBackpressureBlock( )`**](https://github.com/ReactiveX/RxJava/wiki/Backpressure#reactive-pull-backpressure-isnt-magic) — block the Observable's thread until the Observer is ready to accept more items from the Observable (⁇) +* [**`onBackpressureBuffer( )`**](https://github.com/ReactiveX/RxJava/wiki/Backpressure#reactive-pull-backpressure-isnt-magic) — maintain a buffer of all emissions from the source Observable and emit them to downstream Subscribers according to the requests they generate +* [**`onBackpressureDrop( )`**](https://github.com/ReactiveX/RxJava/wiki/Backpressure#reactive-pull-backpressure-isnt-magic) — drop emissions from the source Observable unless there is a pending request from a downstream Subscriber, in which case emit enough items to fulfill the request +* [**`onErrorFlatMap( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#onerrorflatmap) — instructs an Observable to emit a sequence of items whenever it encounters an error (⁇) +* [**`onErrorResumeNext( )`**](https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators#onerrorresumenext) — instructs an Observable to emit a sequence of items if it encounters an error +* [**`onErrorReturn( )`**](https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators#onerrorreturn) — instructs an Observable to emit a particular item when it encounters an error +* [**`onExceptionResumeNext( )`**](https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators#onexceptionresumenext) — instructs an Observable to continue emitting items after it encounters an exception (but not another variety of throwable) +* **`orElse( )`** (scala) — _see [**`defaultIfEmpty( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators)_ +* [**`parallel( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#parallel) — split the work done on the emissions from an Observable into multiple Observables each operating on its own parallel thread (⁇) +* [**`parallelMerge( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#parallelmerge) — combine multiple Observables into smaller number of Observables (⁇) +* [**`pivot( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#pivot) — combine multiple sets of grouped observables so that they are arranged primarily by group rather than by set (⁇) +* [**`publish( )`**](https://github.com/ReactiveX/RxJava/wiki/Connectable-Observable-Operators) — represents an Observable as a Connectable Observable +* [**`publishLast( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#publishlast) — represent an Observable as a Connectable Observable that emits only the last item emitted by the source Observable (⁇) +* [**`range( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#range) — create an Observable that emits a range of sequential integers +* [**`reduce( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#reduce) — apply a function to each emitted item, sequentially, and emit only the final accumulated value +* **`reductions( )`** (clojure) — _see [**`scan( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#scan)_ +* [**`refCount( )`**](https://github.com/ReactiveX/RxJava/wiki/Connectable-Observable-Operators) — makes a Connectable Observable behave like an ordinary Observable +* [**`repeat( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables) — create an Observable that emits a particular item or sequence of items repeatedly +* [**`repeatWhen( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#repeatwhen) — create an Observable that emits a particular item or sequence of items repeatedly, depending on the emissions of a second Observable +* [**`replay( )`**](https://github.com/ReactiveX/RxJava/wiki/Connectable-Observable-Operators#observablereplay) — ensures that all Subscribers see the same sequence of emitted items, even if they subscribe after the Observable begins emitting the items +* **`rest( )`** (clojure) — _see [**`next( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators#next)_ +* **`return( )`** (clojure) — _see [**`just( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#just)_ +* [**`retry( )`**](https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators#retry) — if a source Observable emits an error, resubscribe to it in the hopes that it will complete without error +* [**`retrywhen( )`**](https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators#retrywhen) — if a source Observable emits an error, pass that error to another Observable to determine whether to resubscribe to the source +* [**`runAsync( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) — returns a `StoppableObservable` that emits multiple actions as generated by a specified Action on a Scheduler (`rxjava-async`) +* [**`sample( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#sample) — emit the most recent items emitted by an Observable within periodic time intervals +* [**`scan( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#scan) — apply a function to each item emitted by an Observable, sequentially, and emit each successive value +* **`seq( )`** (clojure) — _see [**`getIterator( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators)_ +* [**`sequenceEqual( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators) — test the equality of sequences emitted by two Observables +* **`sequenceEqualWith( )`** (scala) — _instance version of [**`sequenceEqual( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators)_ +* [**`serialize( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators#serialize) — force an Observable to make serialized calls and to be well-behaved +* **`share( )`** — _see [**`refCount( )`**](https://github.com/ReactiveX/RxJava/wiki/Connectable-Observable-Operators)_ +* [**`single( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`) — if the source Observable completes after emitting a single item, return that item, otherwise throw an exception +* [**`single( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) (`Observable`) — if the source Observable completes after emitting a single item, emit that item, otherwise notify of an exception +* **`singleOption( )`** (scala) — _see [**`singleOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators#single-and-singleordefault) (`BlockingObservable`)_ +* [**`singleOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) (`BlockingObservable`) — if the source Observable completes after emitting a single item, return that item, otherwise return a default item +* [**`singleOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) (`Observable`) — if the source Observable completes after emitting a single item, emit that item, otherwise emit a default item +* **`singleOrElse( )`** (scala) — _see [**`singleOrDefault( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators)_ +* **`size( )`** (scala) — _see [**`count( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#count)_ +* [**`skip( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#skip) — ignore the first _n_ items emitted by an Observable +* [**`skipLast( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#skiplast) — ignore the last _n_ items emitted by an Observable +* [**`skipUntil( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators) — discard items emitted by a source Observable until a second Observable emits an item, then emit the remainder of the source Observable's items +* [**`skipWhile( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators) — discard items emitted by an Observable until a specified condition is false, then emit the remainder +* **`sliding( )`** (scala) — _see [**`window( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#window)_ +* **`slidingBuffer( )`** (scala) — _see [**`buffer( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#buffer)_ +* [**`split( )`**](https://github.com/ReactiveX/RxJava/wiki/String-Observables) (`StringObservable`) — converts an Observable of Strings into an Observable of Strings that treats the source sequence as a stream and splits it on a specified regex boundary +* [**`start( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) — create an Observable that emits the return value of a function (`rxjava-async`) +* [**`startCancellableFuture( )`**](https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#fromcancellablefuture-startcancellablefuture-and-defercancellablefuture) — convert a function that returns Future into an Observable that emits that Future's return value in a way that monitors the subscription status of the Observable to determine whether to halt work on the Future (⁇)(`rxjava-async`) +* [**`startFuture( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) — convert a function that returns Future into an Observable that emits that Future's return value (`rxjava-async`) +* [**`startWith( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#startwith) — emit a specified sequence of items before beginning to emit the items from the Observable +* [**`stringConcat( )`**](https://github.com/ReactiveX/RxJava/wiki/String-Observables) (`StringObservable`) — converts an Observable that emits a sequence of strings into an Observable that emits a single string that concatenates them all +* [**`subscribeOn( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — specify which Scheduler an Observable should use when its subscription is invoked +* [**`sumDouble( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#sumdouble) — adds the Doubles emitted by an Observable and emits this sum (`rxjava-math`) +* [**`sumFloat( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#sumfloat) — adds the Floats emitted by an Observable and emits this sum (`rxjava-math`) +* [**`sumInt( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#sumint) — adds the Integers emitted by an Observable and emits this sum (`rxjava-math`) +* [**`sumLong( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#sumlong) — adds the Longs emitted by an Observable and emits this sum (`rxjava-math`) +* **`switch( )`** (scala) — _see [**`switchOnNext( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#switchonnext)_ +* [**`switchCase( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators) — emit the sequence from a particular Observable based on the results of an evaluation (`contrib-computation-expressions`) +* [**`switchMap( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#switchmap) — transform the items emitted by an Observable into Observables, and mirror those items emitted by the most-recently transformed Observable +* [**`switchOnNext( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#switchonnext) — convert an Observable that emits Observables into a single Observable that emits the items emitted by the most-recently emitted of those Observables +* **`synchronize( )`** — _see [**`serialize( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators)_ +* [**`take( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#take) — emit only the first _n_ items emitted by an Observable +* [**`takeFirst( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) — emit only the first item emitted by an Observable, or the first item that meets some condition +* [**`takeLast( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#takelast) — only emit the last _n_ items emitted by an Observable +* [**`takeLastBuffer( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) — emit the last _n_ items emitted by an Observable, as a single list item +* **`takeRight( )`** (scala) — _see [**`last( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#last) (`Observable`) or [**`takeLast( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#takelast)_ +* [**`takeUntil( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators) — emits the items from the source Observable until a second Observable emits an item +* [**`takeWhile( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators) — emit items emitted by an Observable as long as a specified condition is true, then skip the remainder +* **`take-while( )`** (clojure) — _see [**`takeWhile( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators)_ +* [**`then( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#rxjava-joins) — transform a series of `Pattern` objects via a `Plan` template (`rxjava-joins`) +* [**`throttleFirst( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#throttlefirst) — emit the first items emitted by an Observable within periodic time intervals +* [**`throttleLast( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#throttlelast) — emit the most recent items emitted by an Observable within periodic time intervals +* [**`throttleWithTimeout( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#throttlewithtimeout) — only emit an item from the source Observable after a particular timespan has passed without the Observable emitting any other items +* **`throw( )`** (clojure) — _see [**`error( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#error)_ +* [**`timeInterval( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — emit the time lapsed between consecutive emissions of a source Observable +* [**`timeout( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#timeout) — emit items from a source Observable, but issue an exception if no item is emitted in a specified timespan +* [**`timer( )`**](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#timer) — create an Observable that emits a single item after a given delay +* [**`timestamp( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — attach a timestamp to every item emitted by an Observable +* [**`toAsync( )`**](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) — convert a function or Action into an Observable that executes the function and emits its return value (`rxjava-async`) +* [**`toBlocking( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) — transform an Observable into a BlockingObservable +* **`toBlockingObservable( )`** - _see [**`toBlocking( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators)_ +* [**`toFuture( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) — convert the Observable into a Future +* [**`toIterable( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) — convert the sequence emitted by the Observable into an Iterable +* **`toIterator( )`** — _see [**`getIterator( )`**](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators)_ +* [**`toList( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#tolist) — collect all items from an Observable and emit them as a single List +* [**`toMap( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#tomap) — convert the sequence of items emitted by an Observable into a map keyed by a specified key function +* [**`toMultimap( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#tomultimap) — convert the sequence of items emitted by an Observable into an ArrayList that is also a map keyed by a specified key function +* **`toSeq( )`** (scala) — _see [**`toList( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#tolist)_ +* [**`toSortedList( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#tosortedlist) — collect all items from an Observable and emit them as a single, sorted List +* **`tumbling( )`** (scala) — _see [**`window( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#window)_ +* **`tumblingBuffer( )`** (scala) — _see [**`buffer( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#buffer)_ +* [**`using( )`**](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) — create a disposable resource that has the same lifespan as an Observable +* [**`when( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#rxjava-joins) — convert a series of `Plan` objects into an Observable (`rxjava-joins`) +* **`where( )`** — _see: [**`filter( )`**](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#filter)_ +* [**`whileDo( )`**](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#conditional-operators) — if a condition is true, emit the source Observable's sequence and then repeat the sequence as long as the condition remains true (`contrib-computation-expressions`) +* [**`window( )`**](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#window) — periodically subdivide items from an Observable into Observable windows and emit these windows rather than emitting the items one at a time +* [**`zip( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#zip) — combine sets of items emitted by two or more Observables together via a specified function and emit items based on the results of this function +* **`zipWith( )`** — _instance version of [**`zip( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#zip)_ +* **`zipWithIndex( )`** (scala) — _see [**`zip( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#zip)_ +* **`++`** (scala) — _see [**`concat( )`**](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators)_ +* **`+:`** (scala) — _see [**`startWith( )`**](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables#startwith)_ + +(⁇) — this proposed operator is not part of RxJava 1.0 diff --git a/docs/Async-Operators.md b/docs/Async-Operators.md new file mode 100644 index 0000000000..17e3bda561 --- /dev/null +++ b/docs/Async-Operators.md @@ -0,0 +1,11 @@ +The following operators are part of the distinct `rxjava-async` module. They are used to convert synchronous methods into Observables. + +* [**`start( )`**](http://reactivex.io/documentation/operators/start.html) — create an Observable that emits the return value of a function +* [**`toAsync( )` or `asyncAction( )` or `asyncFunc( )`**](http://reactivex.io/documentation/operators/start.html) — convert a function or Action into an Observable that executes the function and emits its return value +* [**`startFuture( )`**](http://reactivex.io/documentation/operators/start.html) — convert a function that returns Future into an Observable that emits that Future's return value +* [**`deferFuture( )`**](http://reactivex.io/documentation/operators/start.html) — convert a Future that returns an Observable into an Observable, but do not attempt to get the Observable that the Future returns until a Subscriber subscribes +* [**`forEachFuture( )`**](http://reactivex.io/documentation/operators/start.html) — pass Subscriber methods to an Observable but also have it behave like a Future that blocks until it completes +* [**`fromAction( )`**](http://reactivex.io/documentation/operators/start.html) — convert an Action into an Observable that invokes the action and emits its result when a Subscriber subscribes +* [**`fromCallable( )`**](http://reactivex.io/documentation/operators/start.html) — convert a Callable into an Observable that invokes the callable and emits its result or exception when a Subscriber subscribes +* [**`fromRunnable( )`**](http://reactivex.io/documentation/operators/start.html) — convert a Runnable into an Observable that invokes the runable and emits its result when a Subscriber subscribes +* [**`runAsync( )`**](http://reactivex.io/documentation/operators/start.html) — returns a `StoppableObservable` that emits multiple actions as generated by a specified Action on a Scheduler \ No newline at end of file diff --git a/docs/Backpressure-(2.0).md b/docs/Backpressure-(2.0).md new file mode 100644 index 0000000000..6b2f2860af --- /dev/null +++ b/docs/Backpressure-(2.0).md @@ -0,0 +1,503 @@ +*Originally contributed to [StackOverflow Documentation](https://stackoverflow.com/documentation/rx-java/2341/backpressure) (going [defunct](https://meta.stackoverflow.com/questions/354217/sunsetting-documentation/)) by [@akarnokd](https://github.com/akarnokd), revised for version 2.x.* + +# Introduction + +**Backpressure** is when in an `Flowable` processing pipeline, some asynchronous stages can't process the values fast enough and need a way to tell the upstream producer to slow down. + +The classic case of the need for backpressure is when the producer is a hot source: + +```java + PublishProcessor source = PublishProcessor.create(); + + source + .observeOn(Schedulers.computation()) + .subscribe(v -> compute(v), Throwable::printStackTrace); + + for (int i = 0; i < 1_000_000; i++) { + source.onNext(i); + } + + Thread.sleep(10_000); +``` + +In this example, the main thread will produce 1 million items to an end consumer which is processing it on a background thread. It is likely the `compute(int)` method takes some time but the overhead of the `Flowable` operator chain may also add to the time it takes to process items. However, the producing thread with the for loop can't know this and keeps `onNext`ing. + +Internally, asynchronous operators have buffers to hold such elements until they can be processed. In the classical Rx.NET and early RxJava, these buffers were unbounded, meaning that they would likely hold nearly all 1 million elements from the example. The problem starts when there are, for example, 1 billion elements or the same 1 million sequence appears 1000 times in a program, leading to `OutOfMemoryError` and generally slowdowns due to excessive GC overhead. + +Similar to how error-handling became a first-class citizen and received operators to deal with it (via `onErrorXXX` operators), backpressure is another property of dataflows that the programmer has to think about and handle (via `onBackpressureXXX` operators). + +Beyond the `PublishProcessor`above, there are other operators that don't support backpressure, mostly due to functional reasons. For example, the operator `interval` emits values periodically, backpressuring it would lead to shifting in the period relative to a wall clock. + +In modern RxJava, most asynchronous operators now have a bounded internal buffer, like `observeOn` above and any attempt to overflow this buffer will terminate the whole sequence with `MissingBackpressureException`. The documentation of each operator has a description about its backpressure behavior. + +However, backpressure is present more subtly in regular cold sequences (which don't and shouldn't yield `MissingBackpressureException`). If the first example is rewritten: + + Flowable.range(1, 1_000_000) + .observeOn(Schedulers.computation()) + .subscribe(v -> compute(v), Throwable::printStackTrace); + + Thread.sleep(10_000); + +There is no error and everything runs smoothly with small memory usage. The reason for this is that many source operators can "generate" values on demand and thus the operator `observeOn` can tell the `range` generate at most so many values the `observeOn` buffer can hold at once without overflow. + +This negotiation is based on the computer science concept of co-routines (I call you, you call me). The operator `range` sends a callback, in the form of an implementation of the `org.reactivestreams.Subscription` interface, to the `observeOn` by calling its (inner `Subscriber`'s) `onSubscribe`. In return, the `observeOn` calls `Subscription.request(n)` with a value to tell the `range` it is allowed to produce (i.e., `onNext` it) that many **additional** elements. It is then the `observeOn`'s responsibility to call the `request` method in the right time and with the right value to keep the data flowing but not overflowing. + +Expressing backpressure in end-consumers is rarely necessary (because they are synchronous in respect to their immediate upstream and backpressure naturally happens due to call-stack blocking), but it may be easier to understand the workings of it: + +```java + Flowable.range(1, 1_000_000) + .subscribe(new DisposableSubscriber() { + @Override + public void onStart() { + request(1); + } + + public void onNext(Integer v) { + compute(v); + + request(1); + } + + @Override + public void onError(Throwable ex) { + ex.printStackTrace(); + } + + @Override + public void onComplete() { + System.out.println("Done!"); + } + }); +``` + +Here the `onStart` implementation indicates `range` to produce its first value, which is then received in `onNext`. Once the `compute(int)` finishes, the another value is then requested from `range`. In a naive implementation of `range`, such call would recursively call `onNext`, leading to `StackOverflowError` which is of course undesirable. + +To prevent this, operators use so-called trampolining logic that prevents such reentrant calls. In `range`'s terms, it will remember that there was a `request(1)` call while it called `onNext()` and once `onNext()` returns, it will make another round and call `onNext()` with the next integer value. Therefore, if the two are swapped, the example still works the same: + +```java + @Override + public void onNext(Integer v) { + request(1); + + compute(v); + } +``` + +However, this is not true for `onStart`. Although the `Flowable` infrastructure guarantees it will be called at most once on each `Subscriber`, the call to `request(1)` may trigger the emission of an element right away. If one has initialization logic after the call to `request(1)` which is needed by `onNext`, you may end up with exceptions: + +```java + Flowable.range(1, 1_000_000) + .subscribe(new DisposableSubscriber() { + + String name; + + @Override + public void onStart() { + request(1); + + name = "RangeExample"; + } + + @Override + public void onNext(Integer v) { + compute(name.length + v); + + request(1); + } + + // ... rest is the same + }); +``` + +In this synchronous case, a `NullPointerException` will be thrown immediately while still executing `onStart`. A more subtle bug happens if the call to `request(1)` triggers an asynchronous call to `onNext` on some other thread and reading `name` in `onNext` races writing it in `onStart` post `request`. + +Therefore, one should do all field initialization in `onStart` or even before that and call `request()` last. Implementations of `request()` in operators ensure proper happens-before relation (or in other terms, memory release or full fence) when necessary. + +# The onBackpressureXXX operators + +Most developers encounter backpressure when their application fails with `MissingBackpressureException` and the exception usually points to the `observeOn` operator. The actual cause is usually the non-backpressured use of `PublishProcessor`, `timer()` or `interval()` or custom operators created via `create()`. + +There are several ways of dealing with such situations. + +## Increasing the buffer sizes + +Sometimes such overflows happen due to bursty sources. Suddenly, the user taps the screen too quickly and `observeOn`'s default 16-element internal buffer on Android overflows. + +Most backpressure-sensitive operators in the recent versions of RxJava now allow programmers to specify the size of their internal buffers. The relevant parameters are usually called `bufferSize`, `prefetch` or `capacityHint`. Given the overflowing example in the introduction, we can just increase the buffer size of `observeOn` to have enough room for all values. + +```java + PublishProcessor source = PublishProcessor.create(); + + source.observeOn(Schedulers.computation(), 1024 * 1024) + .subscribe(e -> { }, Throwable::printStackTrace); + + for (int i = 0; i < 1_000_000; i++) { + source.onNext(i); + } +``` + +Note however that generally, this may be only a temporary fix as the overflow can still happen if the source overproduces the predicted buffer size. In this case, one can use one of the following operators. + +## Batching/skipping values with standard operators + +In case the source data can be processed more efficiently in batch, one can reduce the likelihood of `MissingBackpressureException` by using one of the standard batching operators (by size and/or by time). + +``` + PublishProcessor source = PublishProcessor.create(); + + source + .buffer(1024) + .observeOn(Schedulers.computation(), 1024) + .subscribe(list -> { + list.parallelStream().map(e -> e * e).first(); + }, Throwable::printStackTrace); + + for (int i = 0; i < 1_000_000; i++) { + source.onNext(i); + } +``` + +If some of the values can be safely ignored, one can use the sampling (with time or another `Flowable`) and throttling operators (`throttleFirst`, `throttleLast`, `throttleWithTimeout`). + +```java + PublishProcessor source = PublishProcessor.create(); + + source + .sample(1, TimeUnit.MILLISECONDS) + .observeOn(Schedulers.computation(), 1024) + .subscribe(v -> compute(v), Throwable::printStackTrace); + + for (int i = 0; i < 1_000_000; i++) { + source.onNext(i); + } +``` + +Note however that these operators only reduce the rate of value reception by the downstream and thus they may still lead to `MissingBackpressureException`. + +## onBackpressureBuffer() + +This operator in its parameterless form reintroduces an unbounded buffer between the upstream source and the downstream operator. Being unbounded means as long as the JVM doesn't run out of memory, it can handle almost any amount coming from a bursty source. + +```java + Flowable.range(1, 1_000_000) + .onBackpressureBuffer() + .observeOn(Schedulers.computation(), 8) + .subscribe(e -> { }, Throwable::printStackTrace); +``` + +In this example, the `observeOn` goes with a very low buffer size yet there is no `MissingBackpressureException` as `onBackpressureBuffer` soaks up all the 1 million values and hands over small batches of it to `observeOn`. + +Note however that `onBackpressureBuffer` consumes its source in an unbounded manner, that is, without applying any backpressure to it. This has the consequence that even a backpressure-supporting source such as `range` will be completely realized. + +There are 4 additional overloads of `onBackpressureBuffer` + +### onBackpressureBuffer(int capacity) + +This is a bounded version that signals `BufferOverflowError`in case its buffer reaches the given capacity. + +```java + Flowable.range(1, 1_000_000) + .onBackpressureBuffer(16) + .observeOn(Schedulers.computation()) + .subscribe(e -> { }, Throwable::printStackTrace); +``` + +The relevance of this operator is decreasing as more and more operators now allow setting their buffer sizes. For the rest, this gives an opportunity to "extend their internal buffer" by having a larger number with `onBackpressureBuffer` than their default. + +### onBackpressureBuffer(int capacity, Action onOverflow) + +This overload calls a (shared) action in case an overflow happens. Its usefulness is rather limited as there is no other information provided about the overflow than the current call stack. + +### onBackpressureBuffer(int capacity, Action onOverflow, BackpressureOverflowStrategy strategy) + +This overload is actually more useful as it let's one define what to do in case the capacity has been reached. The `BackpressureOverflow.Strategy` is an interface actually but the class `BackpressureOverflow` offers 4 static fields with implementations of it representing typical actions: + + - `ON_OVERFLOW_ERROR`: this is the default behavior of the previous two overloads, signalling a `BufferOverflowException` + - `ON_OVERFLOW_DEFAULT`: currently it is the same as `ON_OVERFLOW_ERROR` + - `ON_OVERFLOW_DROP_LATEST` : if an overflow would happen, the current value will be simply ignored and only the old values will be delivered once the downstream requests. + - `ON_OVERFLOW_DROP_OLDEST` : drops the oldest element in the buffer and adds the current value to it. + +```java + Flowable.range(1, 1_000_000) + .onBackpressureBuffer(16, () -> { }, + BufferOverflowStrategy.ON_OVERFLOW_DROP_OLDEST) + .observeOn(Schedulers.computation()) + .subscribe(e -> { }, Throwable::printStackTrace); +``` + +Note that the last two strategies cause discontinuity in the stream as they drop out elements. In addition, they won't signal `BufferOverflowException`. + +## onBackpressureDrop() + +Whenever the downstream is not ready to receive values, this operator will drop that element from the sequence. One can think of it as a 0 capacity `onBackpressureBuffer` with strategy `ON_OVERFLOW_DROP_LATEST`. + +This operator is useful when one can safely ignore values from a source (such as mouse moves or current GPS location signals) as there will be more up-to-date values later on. + +```java + component.mouseMoves() + .onBackpressureDrop() + .observeOn(Schedulers.computation(), 1) + .subscribe(event -> compute(event.x, event.y)); +``` + +It may be useful in conjunction with the source operator `interval()`. For example, if one wants to perform some periodic background task but each iteration may last longer than the period, it is safe to drop the excess interval notification as there will be more later on: + +```java + Flowable.interval(1, TimeUnit.MINUTES) + .onBackpressureDrop() + .observeOn(Schedulers.io()) + .doOnNext(e -> networkCall.doStuff()) + .subscribe(v -> { }, Throwable::printStackTrace); +``` + +There exist one overload of this operator: `onBackpressureDrop(Consumer onDrop)` where the (shared) action is called with the value being dropped. This variant allows cleaning up the values themselves (e.g., releasing associated resources). + +## onBackpressureLatest() + +The final operator keeps only the latest value and practically overwrites older, undelivered values. One can think of this as a variant of the `onBackpressureBuffer` with a capacity of 1 and strategy of `ON_OVERFLOW_DROP_OLDEST`. + +Unlike `onBackpressureDrop` there is always a value available for consumption if the downstream happened to be lagging behind. This can be useful in some telemetry-like situations where the data may come in some bursty pattern but only the very latest is interesting for processing. + +For example, if the user clicks a lot on the screen, we'd still want to react to its latest input. + +```java + component.mouseClicks() + .onBackpressureLatest() + .observeOn(Schedulers.computation()) + .subscribe(event -> compute(event.x, event.y), Throwable::printStackTrace); +``` + +The use of `onBackpressureDrop` in this case would lead to a situation where the very last click gets dropped and leaves the user wondering why the business logic wasn't executed. + +# Creating backpressured datasources + +Creating backpressured data sources is the relatively easier task when dealing with backpressure in general because the library already offers static methods on `Flowable` that handle backpressure for the developer. We can distinguish two kinds of factory methods: cold "generators" that either return and generate elements based on downstream demand and hot "pushers" that usually bridge non-reactive and/or non-backpressurable data sources and layer some backpressure handling on top of them. + +## just + +The most basic backpressure aware source is created via `just`: + +```java + Flowable.just(1).subscribe(new DisposableSubscriber() { + @Override + public void onStart() { + request(0); + } + + @Override + public void onNext(Integer v) { + System.out.println(v); + } + + // the rest is omitted for brevity + } +``` + +Since we explicitly don't request in `onStart`, this will not print anything. `just` is great when there is a constant value we'd like to jump-start a sequence. + +Unfortunately, `just` is often mistaken for a way to compute something dynamically to be consumed by `Subscriber`s: + +```java + int counter; + + int computeValue() { + return ++counter; + } + + Flowable o = Flowable.just(computeValue()); + + o.subscribe(System.out:println); + o.subscribe(System.out:println); +``` + +Surprising to some, this prints 1 twice instead of printing 1 and 2 respectively. If the call is rewritten, it becomes obvious why it works so: + +```java + int temp = computeValue(); + + Flowable o = Flowable.just(temp); +``` + +The `computeValue` is called as part of the main routine and not in response to the subscribers subscribing. + +## fromCallable + +What people actually need is the method `fromCallable`: + +```java + Flowable o = Flowable.fromCallable(() -> computeValue()); +``` + +Here the `computeValue` is executed only when a subscriber subscribes and for each of them, printing the expected 1 and 2. Naturally, `fromCallable` also properly supports backpressure and won't emit the computed value unless requested. Note however that the computation does happen anyway. In case the computation itself should be delayed until the downstream actually requests, we can use `just` with `map`: + +```java + Flowable.just("This doesn't matter").map(ignored -> computeValue())... +``` + +`just` won't emit its constant value until requested when it is mapped to the result of the `computeValue`, still called for each subscriber individually. + +## fromArray + +If the data is already available as an array of objects, a list of objects or any `Iterable` source, the respective `from` overloads will handle the backpressure and emission of such sources: + +```java + Flowable.fromArray(1, 2, 3, 4, 5).subscribe(System.out::println); +``` + +For convenience (and avoiding warnings about generic array creation) there are 2 to 10 argument overloads to `just` that internally delegate to `from`. + +The `fromIterable` also gives an interesting opportunity. Many value generation can be expressed in a form of a state-machine. Each requested element triggers a state transition and computation of the returned value. + +Writing such state machines as `Iterable`s is somewhat complicated (but still easier than writing an `Flowable` for consuming it) and unlike C#, Java doesn't have any support from the compiler to build such state machines by simply writing classically looking code (with `yield return` and `yield break`). Some libraries offer some help, such as Google Guava's `AbstractIterable` and IxJava's `Ix.generate()` and `Ix.forloop()`. These are by themselves worthy of a full series so let's see some very basic `Iterable` source that repeats some constant value indefinitely: + +```java + Iterable iterable = () -> new Iterator() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + return 1; + } + }; + + Flowable.fromIterable(iterable).take(5).subscribe(System.out::println); +``` + +If we'd consume the `iterator` via classic for-loop, that would result in an infinite loop. Since we build an `Flowable` out of it, we can express our will to consume only the first 5 of it and then stop requesting anything. This is the true power of lazily evaluating and computing inside `Flowable`s. + +## generate() + +Sometimes, the data source to be converted into the reactive world itself is synchronous (blocking) and pull-like, that is, we have to call some `get` or `read` method to get the next piece of data. One could, of course, turn that into an `Iterable` but when such sources are associated with resources, we may leak those resources if the downstream unsubscribes the sequence before it would end. + +To handle such cases, RxJava has the `generate` factory method family. + +```java + Flowable o = Flowable.generate( + () -> new FileInputStream("data.bin"), + (inputstream, output) -> { + try { + int abyte = inputstream.read(); + if (abyte < 0) { + output.onComplete(); + } else { + output.onNext(abyte); + } + } catch (IOException ex) { + output.onError(ex); + } + return inputstream; + }, + inputstream -> { + try { + inputstream.close(); + } catch (IOException ex) { + RxJavaPlugins.onError(ex); + } + } + ); +``` + +Generally, `generate` uses 3 callbacks. + +The first callbacks allows one to create a per-subscriber state, such as the `FileInputStream` in the example; the file will be opened independently to each individual subscriber. + +The second callback takes this state object and provides an output `Observer` whose `onXXX` methods can be called to emit values. This callback is executed as many times as the downstream requested. At each invocation, it has to call `onNext` at most once optionally followed by either `onError` or `onComplete`. In the example we call `onComplete()` if the read byte is negative, indicating and end of file, and call `onError` in case the read throws an `IOException`. + +The final callback gets invoked when the downstream unsubscribes (closing the inputstream) or when the previous callback called the terminal methods; it allows freeing up resources. Since not all sources need all these features, the static methods of `Flowable.generate` let's one create instances without them. + +Unfortunately, many method calls across the JVM and other libraries throw checked exceptions and need to be wrapped into `try-catch`es as the functional interfaces used by this class don't allow throwing checked exceptions. + +Of course, we can imitate other typical sources, such as an unbounded range with it: + +```java + Flowable.generate( + () -> 0, + (current, output) -> { + output.onNext(current); + return current + 1; + }, + e -> { } + ); +``` + +In this setup, the `current` starts out with `0` and next time the lambda is invoked, the parameter `current` now holds `1`. + +*(Remark: the 1.x classes `SyncOnSubscribe` and `AsyncOnSubscribe` are no longer available.)* + +## create(emitter) + +Sometimes, the source to be wrapped into an `Flowable` is already hot (such as mouse moves) or cold but not backpressurable in its API (such as an asynchronous network callback). + +To handle such cases, a recent version of RxJava introduced the `create(emitter)` factory method. It takes two parameters: + + - a callback that will be called with an instance of the `Emitter` interface for each incoming subscriber, + - a `BackpressureStrategy` enumeration that mandates the developer to specify the backpressure behavior to be applied. It has the usual modes, similar to `onBackpressureXXX` in addition to signalling a `MissingBackpressureException` or simply ignoring such overflow inside it altogether. + +Note that it currently doesn't support additional parameters to those backpressure modes. If one needs those customization, using `NONE` as the backpressure mode and applying the relevant `onBackpressureXXX` on the resulting `Flowable` is the way to go. + +The first typical case for its use when one wants to interact with a push-based source, such as GUI events. Those APIs feature some form of `addListener`/`removeListener` calls that one can utilize: + +```java + Flowable.create(emitter -> { + ActionListener al = e -> { + emitter.onNext(e); + }; + + button.addActionListener(al); + + emitter.setCancellation(() -> + button.removeListener(al)); + + }, BackpressureStrategy.BUFFER); +``` + +The `Emitter` is relatively straightforward to use; one can call `onNext`, `onError` and `onComplete` on it and the operator handles backpressure and unsubscription management on its own. In addition, if the wrapped API supports cancellation (such as the listener removal in the example), one can use the `setCancellation` (or `setSubscription` for `Subscription`-like resources) to register a cancellation callback that gets invoked when the downstream unsubscribes or the `onError`/`onComplete` is called on the provided `Emitter`instance. + +These methods allow only a single resource to be associated with the emitter at a time and setting a new one unsubscribes the old one automatically. If one has to handle multiple resources, create a `CompositeSubscription`, associate it with the emitter and then add further resources to the `CompositeSubscription` itself: + +```java + Flowable.create(emitter -> { + CompositeSubscription cs = new CompositeSubscription(); + + Worker worker = Schedulers.computation().createWorker(); + + ActionListener al = e -> { + emitter.onNext(e); + }; + + button.addActionListener(al); + + cs.add(worker); + cs.add(Subscriptions.create(() -> + button.removeActionListener(al)); + + emitter.setSubscription(cs); + + }, BackpressureMode.BUFFER); +``` + +The second scenario usually involves some asynchronous, callback-based API that has to be converted into an `Flowable`. + +```java + Flowable.create(emitter -> { + + someAPI.remoteCall(new Callback() { + @Override + public void onSuccess(Data data) { + emitter.onNext(data); + emitter.onComplete(); + } + + @Override + public void onFailure(Exception error) { + emitter.onError(error); + } + }); + + }, BackpressureMode.LATEST); +``` + +In this case, the delegation works the same way. Unfortunately, usually, these classical callback-style APIs don't support cancellation, but if they do, one can setup their cancellation just like in the previoius examples (with perhaps a more involved way though). Note the use of the `LATEST` backpressure mode; if we know there will be only a single value, we don't need the `BUFFER` strategy as it allocates a default 128 element long buffer (that grows as necessary) that is never going to be fully utilized. \ No newline at end of file diff --git a/docs/Backpressure.md b/docs/Backpressure.md new file mode 100644 index 0000000000..8529ec0995 --- /dev/null +++ b/docs/Backpressure.md @@ -0,0 +1,175 @@ +# Introduction + +In RxJava it is not difficult to get into a situation in which an Observable is emitting items more rapidly than an operator or subscriber can consume them. This presents the problem of what to do with such a growing backlog of unconsumed items. + +For example, imagine using the [`zip`](http://reactivex.io/documentation/operators/zip.html) operator to zip together two infinite Observables, one of which emits items twice as frequently as the other. A naive implementation of the `zip` operator would have to maintain an ever-expanding buffer of items emitted by the faster Observable to eventually combine with items emitted by the slower one. This could cause RxJava to seize an unwieldy amount of system resources. + +There are a variety of strategies with which you can exercise flow control and backpressure in RxJava in order to alleviate the problems caused when a quickly-producing Observable meets a slow-consuming observer. This page explains some of these strategies, and also shows you how you can design your own Observables and Observable operators to respect requests for flow control. + +## Hot and cold Observables, and multicasted Observables + +A _cold_ Observable emits a particular sequence of items, but can begin emitting this sequence when its Observer finds it to be convenient, and at whatever rate the Observer desires, without disrupting the integrity of the sequence. For example if you convert a static Iterable into an Observable, that Observable will emit the same sequence of items no matter when it is later subscribed to or how frequently those items are observed. Examples of items emitted by a cold Observable might include the results of a database query, file retrieval, or web request. + +A _hot_ Observable begins generating items to emit immediately when it is created. Subscribers typically begin observing the sequence of items emitted by a hot Observable from somewhere in the middle of the sequence, beginning with the first item emitted by the Observable subsequent to the establishment of the subscription. Such an Observable emits items at its own pace, and it is up to its observers to keep up. Examples of items emitted by a hot Observable might include mouse & keyboard events, system events, or stock prices. + +When a cold Observable is _multicast_ (when it is converted into a `ConnectableObservable` and its [`connect()`](http://reactivex.io/documentation/operators/connect.html) method is called), it effectively becomes _hot_ and for the purposes of backpressure and flow-control it should be treated as a hot Observable. + +Cold Observables are ideal for the reactive pull model of backpressure described below. Hot Observables typically do not cope well with a reactive pull model, and are better candidates for some of the other flow control strategies discussed on this page, such as the use of [the `onBackpressureBuffer` or `onBackpressureDrop` operators](http://reactivex.io/documentation/operators/backpressure.html), throttling, buffers, or windows. + +# Useful operators that avoid the need for backpressure + +Your first line of defense against the problems of over-producing Observables is to use some of the ordinary set of Observable operators to reduce the number of emitted items to a more manageable number. The examples in this section will show how you might use such operators to handle a bursty Observable like the one illustrated in the following marble diagram: + +​ + +By fine-tuning the parameters to these operators you can ensure that a slow-consuming observer is not overwhelmed by a fast-producing Observable. + +## Throttling + +Operators like [`sample( )` or `throttleLast( )`](http://reactivex.io/documentation/operators/sample.html), [`throttleFirst( )`](http://reactivex.io/documentation/operators/sample.html), and [`throttleWithTimeout( )` or `debounce( )`](http://reactivex.io/documentation/operators/debounce.html) allow you to regulate the rate at which an Observable emits items. + +The following diagrams show how you could use each of these operators on the bursty Observable shown above. + +### sample (or throttleLast) +The `sample` operator periodically "dips" into the sequence and emits only the most recently emitted item during each dip: + +​ +```java +Observable burstySampled = bursty.sample(500, TimeUnit.MILLISECONDS); +``` + +### throttleFirst +The `throttleFirst` operator is similar, but emits not the most recently emitted item, but the first item that was emitted after the previous "dip": + +​ +```java +Observable burstyThrottled = bursty.throttleFirst(500, TimeUnit.MILLISECONDS); +``` + +### debounce (or throttleWithTimeout) +The `debounce` operator emits only those items from the source Observable that are not followed by another item within a specified duration: + +​ +```java +Observable burstyDebounced = bursty.debounce(10, TimeUnit.MILLISECONDS); +``` + +## Buffers and windows + +You can also use an operator like [`buffer( )`](http://reactivex.io/documentation/operators/buffer.html) or [`window( )`](http://reactivex.io/documentation/operators/window.html) to collect items from the over-producing Observable and then emit them, less-frequently, as collections (or Observables) of items. The slow consumer can then decide whether to process only one particular item from each collection, to process some combination of those items, or to schedule work to be done on each item in the collection, as appropriate. + +The following diagrams show how you could use each of these operators on the bursty Observable shown above. + +### buffer + +You could, for example, close and emit a buffer of items from the bursty Observable periodically, at a regular interval of time: + +​ +```java +Observable> burstyBuffered = bursty.buffer(500, TimeUnit.MILLISECONDS); +``` + +Or you could get fancy, and collect items in buffers during the bursty periods and emit them at the end of each burst, by using the `debounce` operator to emit a buffer closing indicator to the `buffer` operator: + +​ +```java +// we have to multicast the original bursty Observable so we can use it +// both as our source and as the source for our buffer closing selector: +Observable burstyMulticast = bursty.publish().refCount(); +// burstyDebounced will be our buffer closing selector: +Observable burstyDebounced = burstMulticast.debounce(10, TimeUnit.MILLISECONDS); +// and this, finally, is the Observable of buffers we're interested in: +Observable> burstyBuffered = burstyMulticast.buffer(burstyDebounced); +```` + +### window + +`window` is similar to `buffer`. One variant of `window` allows you to periodically emit Observable windows of items at a regular interval of time: + +​ +```java +Observable> burstyWindowed = bursty.window(500, TimeUnit.MILLISECONDS); +```` + +You could also choose to emit a new window each time you have collected a particular number of items from the source Observable: + +​ +```java +Observable> burstyWindowed = bursty.window(5); +``` + +# Callstack blocking as a flow-control alternative to backpressure + +Another way of handling an overproductive Observable is to block the callstack (parking the thread that governs the overproductive Observable). This has the disadvantage of going against the “reactive” and non-blocking model of Rx. However this can be a viable option if the problematic Observable is on a thread that can be blocked safely. Currently RxJava does not expose any operators to facilitate this. + +If the Observable, all of the operators that operate on it, and the observer that is subscribed to it, are all operating in the same thread, this effectively establishes a form of backpressure by means of callstack blocking. But be aware that many Observable operators do operate in distinct threads by default (the javadocs for those operators will indicate this). + +# How a subscriber establishes “reactive pull” backpressure + +When you subscribe to an `Observable` with a `Subscriber`, you can request reactive pull backpressure by calling `Subscriber.request(n)` in the `Subscriber`’s `onStart()` method (where _n_ is the maximum number of items you want the `Observable` to emit before the next `request()` call). + +Then, after handling this item (or these items) in `onNext()`, you can call `request()` again to instruct the `Observable` to emit another item (or items). Here is an example of a `Subscriber` that requests one item at a time from `someObservable`: + +```java +someObservable.subscribe(new Subscriber() { + @Override + public void onStart() { + request(1); + } + + @Override + public void onCompleted() { + // gracefully handle sequence-complete + } + + @Override + public void onError(Throwable e) { + // gracefully handle error + } + + @Override + public void onNext(T n) { + // do something with the emitted item "n" + // request another item: + request(1); + } +}); +``` + +You can pass a magic number to `request`, `request(Long.MAX_VALUE)`, to disable reactive pull backpressure and to ask the Observable to emit items at its own pace. `request(0)` is a legal call, but has no effect. Passing values less than zero to `request` will cause an exception to be thrown. + +## Reactive pull backpressure isn’t magic + +Backpressure doesn’t make the problem of an overproducing Observable or an underconsuming Subscriber go away. It just moves the problem up the chain of operators to a point where it can be handled better. + +Let’s take a closer look at the problem of the uneven [`zip`](http://reactivex.io/documentation/operators/zip.html). + +You have two Observables, _A_ and _B_, where _B_ is inclined to emit items more frequently than _A_. When you try to `zip` these two Observables together, the `zip` operator combines item _n_ from _A_ and item _n_ from _B_, but meanwhile _B_ has also emitted items _n_+1 to _n_+_m_. The `zip` operator has to hold on to these items so it can combine them with items _n_+1 to _n_+_m_ from _A_ as they are emitted, but meanwhile _m_ keeps growing and so the size of the buffer needed to hold on to these items keeps increasing. + +You could attach a throttling operator to _B_, but this would mean ignoring some of the items _B_ emits, which might not be appropriate. What you’d really like to do is to signal to _B_ that it needs to slow down and then let _B_ decide how to do this in a way that maintains the integrity of its emissions. + +The reactive pull backpressure model lets you do this. It creates a sort of active pull from the Subscriber in contrast to the normal passive push Observable behavior. + +The `zip` operator as implemented in RxJava uses this technique. It maintains a small buffer of items for each source Observable, and it requests no more items from each source Observable than would fill its buffer. Each time `zip` emits an item, it removes the corresponding items from its buffers and requests exactly one more item from each of its source Observables. + +(Many RxJava operators exercise reactive pull backpressure. Some operators do not need to use this variety of backpressure, as they operate in the same thread as the Observable they operate on, and so they exert a form of blocking backpressure simply by not giving the Observable the opportunity to emit another item until they have finished processing the previous one. For other operators, backpressure is inappropriate as they have been explicitly designed to deal with flow control in other ways. The RxJava javadocs for those operators that are methods of the Observable class indicate which ones do not use reactive pull backpressure and why.) + +For this to work, though, Observables _A_ and _B_ must respond correctly to the `request()`. If an Observable has not been written to support reactive pull backpressure (such support is not a requirement for Observables), you can apply one of the following operators to it, each of which forces a simple form of backpressure behavior: + +
+
onBackpressureBuffer
+
maintains a buffer of all emissions from the source Observable and emits them to downstream Subscribers according to the requests they generate

an experimental version of this operator (not available in RxJava 1.0) allows you to set the capacity of the buffer; applying this operator will cause the resulting Observable to terminate with an error if this buffer is overrun​
+
onBackpressureDrop
+
drops emissions from the source Observable unless there is a pending request from a downstream Subscriber, in which case it will emit enough items to fulfill the request
+
onBackpressureBlock (experimental, not in RxJava 1.0)
+
blocks the thread on which the source Observable is operating until such time as a Subscriber issues a request for items, and then unblocks the thread only so long as there are pending requests
+
+ +If you do not apply any of these operators to an Observable that does not support backpressure, _and_ if either you as the Subscriber or some operator between you and the Observable attempts to apply reactive pull backpressure, you will encounter a `MissingBackpressureException` which you will be notified of via your `onError()` callback. + +# Further reading + +If the standard operators aren't providing the expected behavior, [one can write custom operators in RxJava](https://github.com/ReactiveX/RxJava/wiki/Implementing-custom-operators-(draft)). + +# See also +* [RxJava 0.20.0-RC1 release notes](https://github.com/ReactiveX/RxJava/releases/tag/0.20.0-RC1) diff --git a/docs/Blocking-Observable-Operators.md b/docs/Blocking-Observable-Operators.md new file mode 100644 index 0000000000..fe2a640f49 --- /dev/null +++ b/docs/Blocking-Observable-Operators.md @@ -0,0 +1,49 @@ +This section explains the [`BlockingObservable`](http://reactivex.io/RxJava/javadoc/rx/observables/BlockingObservable.html) subclass. A Blocking Observable extends the ordinary Observable class by providing a set of operators on the items emitted by the Observable that block. + +To transform an `Observable` into a `BlockingObservable`, use the [`Observable.toBlocking( )`](http://reactivex.io/RxJava/javadoc/rx/Observable.html#toBlocking()) method or the [`BlockingObservable.from( )`](http://reactivex.io/RxJava/javadoc/rx/observables/BlockingObservable.html#from(rx.Observable)) method. + +* [**`forEach( )`**](http://reactivex.io/documentation/operators/subscribe.html) — invoke a function on each item emitted by the Observable; block until the Observable completes +* [**`first( )`**](http://reactivex.io/documentation/operators/first.html) — block until the Observable emits an item, then return the first item emitted by the Observable +* [**`firstOrDefault( )`**](http://reactivex.io/documentation/operators/first.html) — block until the Observable emits an item or completes, then return the first item emitted by the Observable or a default item if the Observable did not emit an item +* [**`last( )`**](http://reactivex.io/documentation/operators/last.html) — block until the Observable completes, then return the last item emitted by the Observable +* [**`lastOrDefault( )`**](http://reactivex.io/documentation/operators/last.html) — block until the Observable completes, then return the last item emitted by the Observable or a default item if there is no last item +* [**`mostRecent( )`**](http://reactivex.io/documentation/operators/first.html) — returns an iterable that always returns the item most recently emitted by the Observable +* [**`next( )`**](http://reactivex.io/documentation/operators/takelast.html) — returns an iterable that blocks until the Observable emits another item, then returns that item +* [**`latest( )`**](http://reactivex.io/documentation/operators/first.html) — returns an iterable that blocks until or unless the Observable emits an item that has not been returned by the iterable, then returns that item +* [**`single( )`**](http://reactivex.io/documentation/operators/first.html) — if the Observable completes after emitting a single item, return that item, otherwise throw an exception +* [**`singleOrDefault( )`**](http://reactivex.io/documentation/operators/first.html) — if the Observable completes after emitting a single item, return that item, otherwise return a default item +* [**`toFuture( )`**](http://reactivex.io/documentation/operators/to.html) — convert the Observable into a Future +* [**`toIterable( )`**](http://reactivex.io/documentation/operators/to.html) — convert the sequence emitted by the Observable into an Iterable +* [**`getIterator( )`**](http://reactivex.io/documentation/operators/to.html) — convert the sequence emitted by the Observable into an Iterator + +> This documentation accompanies its explanations with a modified form of "marble diagrams." Here is how these marble diagrams represent Blocking Observables: + + + +#### see also: +* javadoc: `BlockingObservable` +* javadoc: `toBlocking()` +* javadoc: `BlockingObservable.from()` + +## Appendix: similar blocking and non-blocking operators + + + + + + + + + + + + + + + + + + + + +
operatorresult when it acts onequivalent in Rx.NET
Observable that emits multiple itemsObservable that emits one itemObservable that emits no items
Observable.firstthe first itemthe single itemNoSuchElementfirstAsync
BlockingObservable.firstthe first itemthe single itemNoSuchElementfirst
Observable.firstOrDefaultthe first itemthe single itemthe default itemfirstOrDefaultAsync
BlockingObservable.firstOrDefaultthe first itemthe single itemthe default itemfirstOrDefault
Observable.lastthe last itemthe single itemNoSuchElementlastAsync
BlockingObservable.lastthe last itemthe single itemNoSuchElementlast
Observable.lastOrDefaultthe last itemthe single itemthe default itemlastOrDefaultAsync
BlockingObservable.lastOrDefaultthe last itemthe single itemthe default itemlastOrDefault
Observable.singleIllegal Argumentthe single itemNoSuchElementsingleAsync
BlockingObservable.singleIllegal Argumentthe single itemNoSuchElementsingle
Observable.singleOrDefaultIllegal Argumentthe single itemthe default itemsingleOrDefaultAsync
BlockingObservable.singleOrDefaultIllegal Argumentthe single itemthe default itemsingleOrDefault
\ No newline at end of file diff --git a/docs/Combining-Observables.md b/docs/Combining-Observables.md new file mode 100644 index 0000000000..fcde91b76b --- /dev/null +++ b/docs/Combining-Observables.md @@ -0,0 +1,166 @@ +This section explains operators you can use to combine multiple Observables. + +# Outline + +- [`combineLatest`](#combinelatest) +- [`join` and `groupJoin`](#joins) +- [`merge`](#merge) +- [`mergeDelayError`](#mergedelayerror) +- [`rxjava-joins`](#rxjava-joins) +- [`startWith`](#startwith) +- [`switchOnNext`](#switchonnext) +- [`zip`](#zip) + +## startWith + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/startwith.html](http://reactivex.io/documentation/operators/startwith.html) + +Emit a specified sequence of items before beginning to emit the items from the Observable. + +#### startWith Example + +```java +Observable names = Observable.just("Spock", "McCoy"); +names.startWith("Kirk").subscribe(item -> System.out.println(item)); + +// prints Kirk, Spock, McCoy +``` + +## merge + +Combines multiple Observables into one. + + +### merge + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/merge.html](http://reactivex.io/documentation/operators/merge.html) + +Combines multiple Observables into one. Any `onError` notifications passed from any of the source observables will immediately be passed through to through to the observers and will terminate the merged `Observable`. + +#### merge Example + +```java +Observable.just(1, 2, 3) + .mergeWith(Observable.just(4, 5, 6)) + .subscribe(item -> System.out.println(item)); + +// prints 1, 2, 3, 4, 5, 6 +``` + +### mergeDelayError + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/merge.html](http://reactivex.io/documentation/operators/merge.html) + +Combines multiple Observables into one. Any `onError` notifications passed from any of the source observables will be withheld until all merged Observables complete, and only then will be passed along to the observers. + +#### mergeDelayError Example + +```java +Observable observable1 = Observable.error(new IllegalArgumentException("")); +Observable observable2 = Observable.just("Four", "Five", "Six"); +Observable.mergeDelayError(observable1, observable2) + .subscribe(item -> System.out.println(item)); + +// emits 4, 5, 6 and then the IllegalArgumentException (in this specific +// example, this throws an `OnErrorNotImplementedException`). +``` + +## zip + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/zip.html](http://reactivex.io/documentation/operators/zip.html) + +Combines sets of items emitted by two or more Observables together via a specified function and emit items based on the results of this function. + +#### zip Example + +```java +Observable firstNames = Observable.just("James", "Jean-Luc", "Benjamin"); +Observable lastNames = Observable.just("Kirk", "Picard", "Sisko"); +firstNames.zipWith(lastNames, (first, last) -> first + " " + last) + .subscribe(item -> System.out.println(item)); + +// prints James Kirk, Jean-Luc Picard, Benjamin Sisko +``` + +## combineLatest + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/combinelatest.html](http://reactivex.io/documentation/operators/combinelatest.html) + +When an item is emitted by either of two Observables, combine the latest item emitted by each Observable via a specified function and emit items based on the results of this function. + +#### combineLatest Example + +```java +Observable newsRefreshes = Observable.interval(100, TimeUnit.MILLISECONDS); +Observable weatherRefreshes = Observable.interval(50, TimeUnit.MILLISECONDS); +Observable.combineLatest(newsRefreshes, weatherRefreshes, + (newsRefreshTimes, weatherRefreshTimes) -> + "Refreshed news " + newsRefreshTimes + " times and weather " + weatherRefreshTimes) + .subscribe(item -> System.out.println(item)); + +// prints: +// Refreshed news 0 times and weather 0 +// Refreshed news 0 times and weather 1 +// Refreshed news 0 times and weather 2 +// Refreshed news 1 times and weather 2 +// Refreshed news 1 times and weather 3 +// Refreshed news 1 times and weather 4 +// Refreshed news 2 times and weather 4 +// Refreshed news 2 times and weather 5 +// ... +``` + +## switchOnNext + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/switch.html](http://reactivex.io/documentation/operators/switch.html) + +Convert an Observable that emits Observables into a single Observable that emits the items emitted by the most-recently emitted of those Observables. + +#### switchOnNext Example + +```java +Observable> timeIntervals = + Observable.interval(1, TimeUnit.SECONDS) + .map(ticks -> Observable.interval(100, TimeUnit.MILLISECONDS) + .map(innerInterval -> "outer: " + ticks + " - inner: " + innerInterval)); +Observable.switchOnNext(timeIntervals) + .subscribe(item -> System.out.println(item)); + +// prints: +// outer: 0 - inner: 0 +// outer: 0 - inner: 1 +// outer: 0 - inner: 2 +// outer: 0 - inner: 3 +// outer: 0 - inner: 4 +// outer: 0 - inner: 5 +// outer: 0 - inner: 6 +// outer: 0 - inner: 7 +// outer: 0 - inner: 8 +// outer: 1 - inner: 0 +// outer: 1 - inner: 1 +// outer: 1 - inner: 2 +// outer: 1 - inner: 3 +// ... +``` + +## joins + +* [**`join( )` and `groupJoin( )`**](http://reactivex.io/documentation/operators/join.html) — combine the items emitted by two Observables whenever one item from one Observable falls within a window of duration specified by an item emitted by the other Observable + +## rxjava-joins + +* (`rxjava-joins`) [**`and( )`, `then( )`, and `when( )`**](http://reactivex.io/documentation/operators/and-then-when.html) — combine sets of items emitted by two or more Observables by means of `Pattern` and `Plan` intermediaries + +> (`rxjava-joins`) — indicates that this operator is currently part of the optional `rxjava-joins` package under `rxjava-contrib` and is not included with the standard RxJava set of operators diff --git a/docs/Conditional-and-Boolean-Operators.md b/docs/Conditional-and-Boolean-Operators.md new file mode 100644 index 0000000000..e6d1358e31 --- /dev/null +++ b/docs/Conditional-and-Boolean-Operators.md @@ -0,0 +1,169 @@ +This section explains operators with which you conditionally emit or transform Observables, or can do boolean evaluations of them: + +### Conditional Operators + +### Outline + +- [`amb`](#all) +- [`defaultIfEmpty`](#defaultIfEmpty) +- [`skipUntil`](#skipUntil) +- [`skipWhile`](#skipWhile) +- [`takeUntil`](#takeUntil) +- [`takeWhile`](#takeUntil) + +## amb + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/amb.html](http://reactivex.io/documentation/operators/amb.html) + +given two or more source Observables, emits all of the items from the first of these Observables to emit an item + +```java + Observable source1 = Observable.range(1, 5); + Observable source2 = Observable.range(6, 5); + Observable.amb(new ArrayList(Arrays.asList(source1, source2))) + .subscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s\n", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` +## defaultIfEmpty + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/defaultifempty.html](http://reactivex.io/documentation/operators/defaultifempty.html) + +emit items from the source Observable, or emit a default item if the source Observable completes after emitting no items + +```java + Observable.empty().defaultIfEmpty(1).blockingSubscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` + +## skipUntil + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/skipuntil.html](http://reactivex.io/documentation/operators/skipuntil.html) + +discard items emitted by a source Observable until a second Observable emits an item, then emit the remainder of the source Observable's items + +```java +Observable observable1 = Observable.range(1, 10).doOnNext(next -> Thread.sleep(1000)); + +observable1.skipUntil(Observable.timer(3, TimeUnit.SECONDS)) + .subscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` +## skipWhile + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/skipwhile.html](http://reactivex.io/documentation/operators/skipwhile.html) + +discard items emitted by an Observable until a specified condition is false, then emit the remainder + +```java +Observable.range(1, 10).skipWhile(next -> next < 5) + .subscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` + +## takeUntil + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/takeuntil.html](http://reactivex.io/documentation/operators/takeuntil.html) + +emits the items from the source Observable until a second Observable emits an item or issues a notification + +```java +Observable.range(1, 10).takeUntil(value -> value >= 5) + .subscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` + +## takeWhile + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/takewhile.html](http://reactivex.io/documentation/operators/takewhile.html) + +emit items emitted by an Observable as long as a specified condition is true, then skip the remainder + +```java + Observable.range(1, 10).takeWhile(value -> value <= 5) + .subscribe(next -> System.out.printf("next: %s\n", next), // onNext + throwable -> System.out.printf("error: %s", throwable), //onError + () -> System.out.println("Completed") //onComplete + ); +``` + +### Boolean Operators + +### Outline + +- [`all`](#all) +- [`contains`](#contains) +- [`isEmpty`](#isEmpty) +- [`sequenceEqual`](#sequenceEqual) + +## all +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/all.html](http://reactivex.io/documentation/operators/all.html) + +determine whether all items emitted by an Observable meet some criteria + +```java +Flowable.range(0,10).doOnNext(next -> System.out.println(next)).all(integer -> integer<10). + blockingSubscribe(success->System.out.println("Success: "+success)); +``` + +## contains +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/contains.html](http://reactivex.io/documentation/operators/contains.html) + +determine whether an Observable emits a particular item or not + +```java +Flowable.range(1,10).doOnNext(next->System.out.println(next)) + .contains(4).blockingSubscribe(contains->System.out.println("contains: "+contains)); +``` + +## isEmpty +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/contains.html](http://reactivex.io/documentation/operators/contains.html) + +determine whether the source Publisher is empty + +```java +Flowable.empty().isEmpty().subscribe(isEmpty -> System.out.printf("isEmpty: %s", isEmpty)); +``` + +## sequenceEqual +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/sequenceequal.html](http://reactivex.io/documentation/operators/sequenceequal.html) + +test the equality of the sequences emitted by two Observables + +```java +Flowable flowable1 = Flowable.range(1,3).doOnNext(next-> System.out.print("flowable1: "+next + " ")); + +Flowable flowable2 = Flowable.range(1,3).doOnNext(next-> System.out.println("flowable2: "+next)); + +Flowable.sequenceEqual(Flowable.fromPublisher(flowable1),Flowable.fromPublisher(flowable2)) + .blockingSubscribe(sequenceEqual->System.out.println("sequenceEqual: "+sequenceEqual)); +``` diff --git a/docs/Connectable-Observable-Operators.md b/docs/Connectable-Observable-Operators.md new file mode 100644 index 0000000000..157ded821b --- /dev/null +++ b/docs/Connectable-Observable-Operators.md @@ -0,0 +1,70 @@ +This section explains the [`ConnectableObservable`](http://reactivex.io/RxJava/javadoc/rx/observables/ConnectableObservable.html) subclass and its operators: + +* [**`ConnectableObservable.connect( )`**](http://reactivex.io/documentation/operators/connect.html) — instructs a Connectable Observable to begin emitting items +* [**`Observable.publish( )`**](http://reactivex.io/documentation/operators/publish.html) — represents an Observable as a Connectable Observable +* [**`Observable.replay( )`**](http://reactivex.io/documentation/operators/replay.html) — ensures that all Subscribers see the same sequence of emitted items, even if they subscribe after the Observable begins emitting the items +* [**`ConnectableObservable.refCount( )`**](http://reactivex.io/documentation/operators/refcount.html) — makes a Connectable Observable behave like an ordinary Observable + +A Connectable Observable resembles an ordinary Observable, except that it does not begin emitting items when it is subscribed to, but only when its `connect()` method is called. In this way you can wait for all intended Subscribers to subscribe to the Observable before the Observable begins emitting items. + + + +The following example code shows two Subscribers subscribing to the same Observable. In the first case, they subscribe to an ordinary Observable; in the second case, they subscribe to a Connectable Observable that only connects after both Subscribers subscribe. Note the difference in the output: + +**Example #1:** +```java +Observable firstMillion = Observable.range(1, 1000000).sample(7, java.util.concurrent.TimeUnit.MILLISECONDS); + +firstMillion.subscribe(next -> System.out.println("Subscriber #1: " + next), // onNext + throwable -> System.out.println("Error: " + throwable), // onError + () -> System.out.println("Sequence #1 complete") // onComplete + ); +firstMillion.subscribe(next -> System.out.println("Subscriber #2: " + next), // onNext + throwable -> System.out.println("Error: " + throwable), // onError + () -> System.out.println("Sequence #2 complete") // onComplete + ); +``` +``` +Subscriber #1:211128 +Subscriber #1:411633 +Subscriber #1:629605 +Subscriber #1:841903 +Sequence #1 complete +Subscriber #2:244776 +Subscriber #2:431416 +Subscriber #2:621647 +Subscriber #2:826996 +Sequence #2 complete +``` +**Example #2:** +```java +ConnectableObservable firstMillion = Observable.range(1, 1000000).sample(7, java.util.concurrent.TimeUnit.MILLISECONDS).publish(); + +firstMillion.subscribe(next -> System.out.println("Subscriber #1: " + next), // onNext + throwable -> System.out.println("Error: " + throwable), // onError + () -> System.out.println("Sequence #1 complete") // onComplete + ); + +firstMillion.subscribe(next -> System.out.println("Subscriber #2: " + next), // onNext + throwable -> System.out.println("Error: " + throwable), // onError + () -> System.out.println("Sequence #2 complete") // onComplete + ); + +firstMillion.connect(); +``` +``` +Subscriber #2:208683 +Subscriber #1:208683 +Subscriber #2:432509 +Subscriber #1:432509 +Subscriber #2:644270 +Subscriber #1:644270 +Subscriber #2:887885 +Subscriber #1:887885 +Sequence #2 complete +Sequence #1 complete +``` + +#### see also: +* javadoc: `ConnectableObservable` +* Introduction to Rx: Publish and Connect \ No newline at end of file diff --git a/docs/Creating-Observables.md b/docs/Creating-Observables.md new file mode 100644 index 0000000000..360e03ab09 --- /dev/null +++ b/docs/Creating-Observables.md @@ -0,0 +1,426 @@ +This page shows methods that create reactive sources, such as `Observable`s. + +### Outline + +- [`create`](#create) +- [`defer`](#defer) +- [`empty`](#empty) +- [`error`](#error) +- [`from`](#from) +- [`generate`](#generate) +- [`interval`](#interval) +- [`just`](#just) +- [`never`](#never) +- [`range`](#range) +- [`timer`](#timer) + +## just + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/just.html](http://reactivex.io/documentation/operators/just.html) + +Constructs a reactive type by taking a pre-existing object and emitting that specific object to the downstream consumer upon subscription. + +#### just example: + +```java +String greeting = "Hello world!"; + +Observable observable = Observable.just(greeting); + +observable.subscribe(item -> System.out.println(item)); +``` + +There exist overloads with 2 to 9 arguments for convenience, which objects (with the same common type) will be emitted in the order they are specified. + +```java +Observable observable = Observable.just("1", "A", "3.2", "def"); + + observable.subscribe(item -> System.out.print(item), error -> error.printStackTrace(), + () -> System.out.println()); +``` + +## From + +Constructs a sequence from a pre-existing source or generator type. + +*Note: These static methods use the postfix naming convention (i.e., the argument type is repeated in the method name) to avoid overload resolution ambiguities.* + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/from.html](http://reactivex.io/documentation/operators/from.html) + +### fromIterable + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +Signals the items from a `java.lang.Iterable` source (such as `List`s, `Set`s or `Collection`s or custom `Iterable`s) and then completes the sequence. + +#### fromIterable example: + +```java +List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8)); + +Observable observable = Observable.fromIterable(list); + +observable.subscribe(item -> System.out.println(item), error -> error.printStackTrace(), + () -> System.out.println("Done")); +``` + +### fromArray + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +Signals the elements of the given array and then completes the sequence. + +#### fromArray example: + +```java +Integer[] array = new Integer[10]; +for (int i = 0; i < array.length; i++) { + array[i] = i; +} + +Observable observable = Observable.fromArray(array); + +observable.subscribe(item -> System.out.println(item), error -> error.printStackTrace(), + () -> System.out.println("Done")); +``` + +*Note: RxJava does not support primitive arrays, only (generic) reference arrays.* + +### fromCallable + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +When a consumer subscribes, the given `java.util.concurrent.Callable` is invoked and its returned value (or thrown exception) is relayed to that consumer. + +#### fromCallable example: + +```java +Callable callable = () -> { + System.out.println("Hello World!"); + return "Hello World!"); +} + +Observable observable = Observable.fromCallable(callable); + +observable.subscribe(item -> System.out.println(item), error -> error.printStackTrace(), + () -> System.out.println("Done")); +``` + +*Remark: In `Completable`, the actual returned value is ignored and the `Completable` simply completes.* + +## fromAction + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +When a consumer subscribes, the given `io.reactivex.function.Action` is invoked and the consumer completes or receives the exception the `Action` threw. + +#### fromAction example: + +```java +Action action = () -> System.out.println("Hello World!"); + +Completable completable = Completable.fromAction(action); + +completable.subscribe(() -> System.out.println("Done"), error -> error.printStackTrace()); +``` + +*Note: the difference between `fromAction` and `fromRunnable` is that the `Action` interface allows throwing a checked exception while the `java.lang.Runnable` does not.* + +## fromRunnable + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +When a consumer subscribes, the given `io.reactivex.function.Action` is invoked and the consumer completes or receives the exception the `Action` threw. + +#### fromRunnable example: + +```java +Runnable runnable = () -> System.out.println("Hello World!"); + +Completable completable = Completable.fromRunnable(runnable); + +completable.subscribe(() -> System.out.println("Done"), error -> error.printStackTrace()); +``` + +*Note: the difference between `fromAction` and `fromRunnable` is that the `Action` interface allows throwing a checked exception while the `java.lang.Runnable` does not.* + +### fromFuture + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +Given a pre-existing, already running or already completed `java.util.concurrent.Future`, wait for the `Future` to complete normally or with an exception in a blocking fashion and relay the produced value or exception to the consumers. + +#### fromFuture example: + +```java +ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + +Future future = executor.schedule(() -> "Hello world!", 1, TimeUnit.SECONDS); + +Observable observable = Observable.fromFuture(future); + +observable.subscribe( + item -> System.out.println(item), + error -> error.printStackTrace(), + () -> System.out.println("Done")); + +executor.shutdown(); +``` + +### from{reactive type} + +Wraps or converts another reactive type to the target reactive type. + +The following combinations are available in the various reactive types with the following signature pattern: `targetType.from{sourceType}()` + +**Available in:** + +targetType \ sourceType | Publisher | Observable | Maybe | Single | Completable +----|---------------|-----------|---------|-----------|---------------- +Flowable | ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) | | | | | +Observable | ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) | | | | | +Maybe | | | | ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) | ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) +Single | ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) | ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) | | | +Completable | ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) | ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) | ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) | ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) | + +*Note: not all possible conversion is implemented via the `from{reactive type}` method families. Check out the `to{reactive type}` method families for further conversion possibilities. + +#### from{reactive type} example: + +```java +Flux reactorFlux = Flux.fromCompletionStage(CompletableFuture.completedFuture(1)); + +Observable observable = Observable.fromPublisher(reactorFlux); + +observable.subscribe( + item -> System.out.println(item), + error -> error.printStackTrace(), + () -> System.out.println("Done")); +``` + +## generate + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/create.html](http://reactivex.io/documentation/operators/create.html) + +Creates a cold, synchronous and stateful generator of values. + +#### generate example: + +```java +int startValue = 1; +int incrementValue = 1; +Flowable flowable = Flowable.generate(() -> startValue, (s, emitter) -> { + int nextValue = s + incrementValue; + emitter.onNext(nextValue); + return nextValue; +}); +flowable.subscribe(value -> System.out.println(value)); +``` + +## create + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/create.html](http://reactivex.io/documentation/operators/create.html) + +Construct a **safe** reactive type instance which when subscribed to by a consumer, runs an user-provided function and provides a type-specific `Emitter` for this function to generate the signal(s) the designated business logic requires. This method allows bridging the non-reactive, usually listener/callback-style world, with the reactive world. + +#### create example: + +```java +ScheduledExecutorService executor = Executors.newSingleThreadedScheduledExecutor(); + +ObservableOnSubscribe handler = emitter -> { + + Future future = executor.schedule(() -> { + emitter.onNext("Hello"); + emitter.onNext("World"); + emitter.onComplete(); + return null; + }, 1, TimeUnit.SECONDS); + + emitter.setCancellable(() -> future.cancel(false)); +}; + +Observable observable = Observable.create(handler); + +observable.subscribe(item -> System.out.println(item), error -> error.printStackTrace(), + () -> System.out.println("Done")); + +Thread.sleep(2000); +executor.shutdown(); +``` + +*Note: `Flowable.create()` must also specify the backpressure behavior to be applied when the user-provided function generates more items than the downstream consumer has requested.* + +## defer + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/defer.html](http://reactivex.io/documentation/operators/defer.html) + +Calls an user-provided `java.util.concurrent.Callable` when a consumer subscribes to the reactive type so that the `Callable` can generate the actual reactive instance to relay signals from towards the consumer. `defer` allows: + +- associating a per-consumer state with such generated reactive instances, +- allows executing side-effects before an actual/generated reactive instance gets subscribed to, +- turn hot sources (i.e., `Subject`s and `Processor`s) into cold sources by basically making those hot sources not exist until a consumer subscribes. + +#### defer example: + +```java +Observable observable = Observable.defer(() -> { + long time = System.currentTimeMillis(); + return Observable.just(time); +}); + +observable.subscribe(time -> System.out.println(time)); + +Thread.sleep(1000); + +observable.subscribe(time -> System.out.println(time)); +``` + +## range + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/range.html](http://reactivex.io/documentation/operators/range.html) + +Generates a sequence of values to each individual consumer. The `range()` method generates `Integer`s, the `rangeLong()` generates `Long`s. + +#### range example: +```java +String greeting = "Hello World!"; + +Observable indexes = Observable.range(0, greeting.length()); + +Observable characters = indexes + .map(index -> greeting.charAt(index)); + +characters.subscribe(character -> System.out.print(character), error -> error.printStackTrace(), + () -> System.out.println()); +``` + +## interval + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/interval.html](http://reactivex.io/documentation/operators/interval.html) + +Periodically generates an infinite, ever increasing numbers (of type `Long`). The `intervalRange` variant generates a limited amount of such numbers. + +#### interval example: + +```java +Observable clock = Observable.interval(1, TimeUnit.SECONDS); + +clock.subscribe(time -> { + if (time % 2 == 0) { + System.out.println("Tick"); + } else { + System.out.println("Tock"); + } +}); +``` + +## timer + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/timer.html](http://reactivex.io/documentation/operators/timer.html) + +After the specified time, this reactive source signals a single `0L` (then completes for `Flowable` and `Observable`). + +#### timer example: + +```java +Observable eggTimer = Observable.timer(5, TimeUnit.MINUTES); + +eggTimer.blockingSubscribe(v -> System.out.println("Egg is ready!")); +``` + +## empty + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/empty-never-throw.html](http://reactivex.io/documentation/operators/empty-never-throw.html) + +This type of source signals completion immediately upon subscription. + +#### empty example: + +```java +Observable empty = Observable.empty(); + +empty.subscribe( + v -> System.out.println("This should never be printed!"), + error -> System.out.println("Or this!"), + () -> System.out.println("Done will be printed.")); +``` + +## never + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/empty-never-throw.html](http://reactivex.io/documentation/operators/empty-never-throw.html) + +This type of source does not signal any `onNext`, `onSuccess`, `onError` or `onComplete`. This type of reactive source is useful in testing or "disabling" certain sources in combinator operators. + +#### never example: + +```java +Observable never = Observable.never(); + +never.subscribe( + v -> System.out.println("This should never be printed!"), + error -> System.out.println("Or this!"), + () -> System.out.println("This neither!")); +``` + +## error + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/empty-never-throw.html](http://reactivex.io/documentation/operators/empty-never-throw.html) + +Signal an error, either pre-existing or generated via a `java.util.concurrent.Callable`, to the consumer. + +#### error example: + +```java +Observable error = Observable.error(new IOException()); + +error.subscribe( + v -> System.out.println("This should never be printed!"), + e -> e.printStackTrace(), + () -> System.out.println("This neither!")); +``` + +A typical use case is to conditionally map or suppress an exception in a chain utilizing `onErrorResumeNext`: + +```java +Observable observable = Observable.fromCallable(() -> { + if (Math.random() < 0.5) { + throw new IOException(); + } + throw new IllegalArgumentException(); +}); + +Observable result = observable.onErrorResumeNext(error -> { + if (error instanceof IllegalArgumentException) { + return Observable.empty(); + } + return Observable.error(error); +}); + +for (int i = 0; i < 10; i++) { + result.subscribe( + v -> System.out.println("This should never be printed!"), + error -> error.printStackTrace(), + () -> System.out.println("Done")); +} +``` \ No newline at end of file diff --git a/docs/Error-Handling-Operators.md b/docs/Error-Handling-Operators.md new file mode 100644 index 0000000000..0b2eace61f --- /dev/null +++ b/docs/Error-Handling-Operators.md @@ -0,0 +1,284 @@ +There are a variety of operators that you can use to react to or recover from `onError` notifications from reactive sources, such as `Observable`s. For example, you might: + +1. swallow the error and switch over to a backup Observable to continue the sequence +1. swallow the error and emit a default item +1. swallow the error and immediately try to restart the failed Observable +1. swallow the error and try to restart the failed Observable after some back-off interval + +# Outline + +- [`doOnError`](#doonerror) +- [`onErrorComplete`](#onerrorcomplete) +- [`onErrorResumeNext`](#onerrorresumenext) +- [`onErrorReturn`](#onerrorreturn) +- [`onErrorReturnItem`](#onerrorreturnitem) +- [`onExceptionResumeNext`](#onexceptionresumenext) +- [`retry`](#retry) +- [`retryUntil`](#retryuntil) +- [`retryWhen`](#retrywhen) + +## doOnError + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/do.html](http://reactivex.io/documentation/operators/do.html) + +Instructs a reactive type to invoke the given `io.reactivex.functions.Consumer` when it encounters an error. + +### doOnError example + +```java +Observable.error(new IOException("Something went wrong")) + .doOnError(error -> System.err.println("The error message is: " + error.getMessage())) + .subscribe( + x -> System.out.println("onNext should never be printed!"), + Throwable::printStackTrace, + () -> System.out.println("onComplete should never be printed!")); +``` + +## onErrorComplete + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/catch.html](http://reactivex.io/documentation/operators/catch.html) + +Instructs a reactive type to swallow an error event and replace it by a completion event. + +Optionally, a `io.reactivex.functions.Predicate` can be specified that gives more control over when an error event should be replaced by a completion event, and when not. + +### onErrorComplete example + +```java +Completable.fromAction(() -> { + throw new IOException(); +}).onErrorComplete(error -> { + // Only ignore errors of type java.io.IOException. + return error instanceof IOException; +}).subscribe( + () -> System.out.println("IOException was ignored"), + error -> System.err.println("onError should not be printed!")); +``` + +## onErrorResumeNext + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/catch.html](http://reactivex.io/documentation/operators/catch.html) + +Instructs a reactive type to emit a sequence of items if it encounters an error. + +### onErrorResumeNext example + +```java + Observable numbers = Observable.generate(() -> 1, (state, emitter) -> { + emitter.onNext(state); + + return state + 1; +}); + +numbers.scan(Math::multiplyExact) + .onErrorResumeNext(Observable.empty()) + .subscribe( + System.out::println, + error -> System.err.println("onError should not be printed!")); + +// prints: +// 1 +// 2 +// 6 +// 24 +// 120 +// 720 +// 5040 +// 40320 +// 362880 +// 3628800 +// 39916800 +// 479001600 +``` + +## onErrorReturn + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/catch.html](http://reactivex.io/documentation/operators/catch.html) + +Instructs a reactive type to emit the item returned by the specified `io.reactivex.functions.Function` when it encounters an error. + +### onErrorReturn example + +```java +Single.just("2A") + .map(v -> Integer.parseInt(v, 10)) + .onErrorReturn(error -> { + if (error instanceof NumberFormatException) return 0; + else throw new IllegalArgumentException(); + }) + .subscribe( + System.out::println, + error -> System.err.println("onError should not be printed!")); + +// prints 0 +``` + +## onErrorReturnItem + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/catch.html](http://reactivex.io/documentation/operators/catch.html) + +Instructs a reactive type to emit a particular item when it encounters an error. + +### onErrorReturnItem example + +```java +Single.just("2A") + .map(v -> Integer.parseInt(v, 10)) + .onErrorReturnItem(0) + .subscribe( + System.out::println, + error -> System.err.println("onError should not be printed!")); + +// prints 0 +``` + +## onExceptionResumeNext + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/catch.html](http://reactivex.io/documentation/operators/catch.html) + +Instructs a reactive type to continue emitting items after it encounters an `java.lang.Exception`. Unlike [`onErrorResumeNext`](#onerrorresumenext), this one lets other types of `Throwable` continue through. + +### onExceptionResumeNext example + +```java +Observable exception = Observable.error(IOException::new) + .onExceptionResumeNext(Observable.just("This value will be used to recover from the IOException")); + +Observable error = Observable.error(Error::new) + .onExceptionResumeNext(Observable.just("This value will not be used")); + +Observable.concat(exception, error) + .subscribe( + message -> System.out.println("onNext: " + message), + err -> System.err.println("onError: " + err)); + +// prints: +// onNext: This value will be used to recover from the IOException +// onError: java.lang.Error +``` + +## retry + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/retry.html](http://reactivex.io/documentation/operators/retry.html) + +Instructs a reactive type to resubscribe to the source reactive type if it encounters an error in the hopes that it will complete without error. + +### retry example + +```java +Observable source = Observable.interval(0, 1, TimeUnit.SECONDS) + .flatMap(x -> { + if (x >= 2) return Observable.error(new IOException("Something went wrong!")); + else return Observable.just(x); + }); + +source.retry((retryCount, error) -> retryCount < 3) + .blockingSubscribe( + x -> System.out.println("onNext: " + x), + error -> System.err.println("onError: " + error.getMessage())); + +// prints: +// onNext: 0 +// onNext: 1 +// onNext: 0 +// onNext: 1 +// onNext: 0 +// onNext: 1 +// onError: Something went wrong! +``` + +## retryUntil + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/retry.html](http://reactivex.io/documentation/operators/retry.html) + +Instructs a reactive type to resubscribe to the source reactive type if it encounters an error until the given `io.reactivex.functions.BooleanSupplier` returns `true`. + +### retryUntil example + +```java +LongAdder errorCounter = new LongAdder(); +Observable source = Observable.interval(0, 1, TimeUnit.SECONDS) + .flatMap(x -> { + if (x >= 2) return Observable.error(new IOException("Something went wrong!")); + else return Observable.just(x); + }) + .doOnError((error) -> errorCounter.increment()); + +source.retryUntil(() -> errorCounter.intValue() >= 3) + .blockingSubscribe( + x -> System.out.println("onNext: " + x), + error -> System.err.println("onError: " + error.getMessage())); + +// prints: +// onNext: 0 +// onNext: 1 +// onNext: 0 +// onNext: 1 +// onNext: 0 +// onNext: 1 +// onError: Something went wrong! +``` + +## retryWhen + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/retry.html](http://reactivex.io/documentation/operators/retry.html) + +Instructs a reactive type to pass any error to another `Observable` or `Flowable` to determine whether to resubscribe to the source. + +### retryWhen example + +```java +Observable source = Observable.interval(0, 1, TimeUnit.SECONDS) + .flatMap(x -> { + if (x >= 2) return Observable.error(new IOException("Something went wrong!")); + else return Observable.just(x); + }); + +source.retryWhen(errors -> { + return errors.map(error -> 1) + + // Count the number of errors. + .scan(Math::addExact) + + .doOnNext(errorCount -> System.out.println("No. of errors: " + errorCount)) + + // Limit the maximum number of retries. + .takeWhile(errorCount -> errorCount < 3) + + // Signal resubscribe event after some delay. + .flatMapSingle(errorCount -> Single.timer(errorCount, TimeUnit.SECONDS)); +}).blockingSubscribe( + x -> System.out.println("onNext: " + x), + Throwable::printStackTrace, + () -> System.out.println("onComplete")); + +// prints: +// onNext: 0 +// onNext: 1 +// No. of errors: 1 +// onNext: 0 +// onNext: 1 +// No. of errors: 2 +// onNext: 0 +// onNext: 1 +// No. of errors: 3 +// onComplete +``` diff --git a/docs/Error-Handling.md b/docs/Error-Handling.md new file mode 100644 index 0000000000..3de9c396d9 --- /dev/null +++ b/docs/Error-Handling.md @@ -0,0 +1,24 @@ +An Observable typically does not _throw_ exceptions. Instead it notifies any observers that an unrecoverable error has occurred by terminating the Observable sequence with an `onError` notification. + +There are some exceptions to this. For example, if the `onError()` call _itself_ fails, the Observable will not attempt to notify the observer of this by again calling `onError` but will throw a `RuntimeException`, an `OnErrorFailedException`, or an `OnErrorNotImplementedException`. + +# Techniques for recovering from onError notifications + +So rather than _catch_ exceptions, your observer or operator should more typically respond to `onError` notifications of exceptions. There are also a variety of Observable operators that you can use to react to or recover from `onError` notifications from Observables. For example, you might use an operator to: + +1. swallow the error and switch over to a backup Observable to continue the sequence +1. swallow the error and emit a default item +1. swallow the error and immediately try to restart the failed Observable +1. swallow the error and try to restart the failed Observable after some back-off interval + +You can use the operators described in [[Error Handling Operators]] to implement these strategies. + +# RxJava-specific exceptions and what to do about them + +
+
CompositeException
This indicates that more than one exception occurred. You can use the exception’s getExceptions() method to retrieve the individual exceptions that make up the composite.
+
MissingBackpressureException
This indicates that a Subscriber or operator attempted to apply reactive pull backpressure to an Observable that does not implement it. See [[Backpressure]] for work-arounds for Observables that do not implement reactive pull backpressure.
+
OnErrorFailedException
This indicates that an Observable tried to call its observer’s onError() method, but that method itself threw an exception.
+
OnErrorNotImplementedException
This indicates that an Observable tried to call its observer’s onError() method, but that no such method existed. You can eliminate this by either fixing the Observable so that it no longer reaches an error condition, by implementing an onError handler in the observer, or by intercepting the onError notification before it reaches the observer by using one of the operators described elsewhere on this page.
+
OnErrorThrowable
Observers pass throwables of this sort into their observers’ onError() handlers. A Throwable of this variety contains more information about the error and about the Observable-specific state of the system at the time of the error than does a standard Throwable.
+
\ No newline at end of file diff --git a/docs/Filtering-Observables.md b/docs/Filtering-Observables.md new file mode 100644 index 0000000000..512b69ba8a --- /dev/null +++ b/docs/Filtering-Observables.md @@ -0,0 +1,760 @@ +This page shows operators you can use to filter and select items emitted by reactive sources, such as `Observable`s. + +# Outline + +- [`debounce`](#debounce) +- [`distinct`](#distinct) +- [`distinctUntilChanged`](#distinctuntilchanged) +- [`elementAt`](#elementat) +- [`elementAtOrError`](#elementatorerror) +- [`filter`](#filter) +- [`first`](#first) +- [`firstElement`](#firstelement) +- [`firstOrError`](#firstorerror) +- [`ignoreElement`](#ignoreelement) +- [`ignoreElements`](#ignoreelements) +- [`last`](#last) +- [`lastElement`](#lastelement) +- [`lastOrError`](#lastorerror) +- [`ofType`](#oftype) +- [`sample`](#sample) +- [`skip`](#skip) +- [`skipLast`](#skiplast) +- [`take`](#take) +- [`takeLast`](#takelast) +- [`throttleFirst`](#throttlefirst) +- [`throttleLast`](#throttlelast) +- [`throttleLatest`](#throttlelatest) +- [`throttleWithTimeout`](#throttlewithtimeout) +- [`timeout`](#timeout) + +## debounce + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/debounce.html](http://reactivex.io/documentation/operators/debounce.html) + +Drops items emitted by a reactive source that are followed by newer items before the given timeout value expires. The timer resets on each emission. + +This operator keeps track of the most recent emitted item, and emits this item only when enough time has passed without the source emitting any other items. + +### debounce example + +```java +// Diagram: +// -A--------------B----C-D-------------------E-|----> +// a---------1s +// b---------1s +// c---------1s +// d---------1s +// e-|----> +// -----------A---------------------D-----------E-|--> + +Observable source = Observable.create(emitter -> { + emitter.onNext("A"); + + Thread.sleep(1_500); + emitter.onNext("B"); + + Thread.sleep(500); + emitter.onNext("C"); + + Thread.sleep(250); + emitter.onNext("D"); + + Thread.sleep(2_000); + emitter.onNext("E"); + emitter.onComplete(); +}); + +source.subscribeOn(Schedulers.io()) + .debounce(1, TimeUnit.SECONDS) + .blockingSubscribe( + item -> System.out.println("onNext: " + item), + Throwable::printStackTrace, + () -> System.out.println("onComplete")); + +// prints: +// onNext: A +// onNext: D +// onNext: E +// onComplete +``` + +## distinct + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/distinct.html](http://reactivex.io/documentation/operators/distinct.html) + +Filters a reactive source by only emitting items that are distinct by comparison from previous items. A `io.reactivex.functions.Function` can be specified that projects each item emitted by the source into a new value that will be used for comparison with previous projected values. + +### distinct example + +```java +Observable.just(2, 3, 4, 4, 2, 1) + .distinct() + .subscribe(System.out::println); + +// prints: +// 2 +// 3 +// 4 +// 1 +``` + +## distinctUntilChanged + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/distinct.html](http://reactivex.io/documentation/operators/distinct.html) + +Filters a reactive source by only emitting items that are distinct by comparison from their immediate predecessors. A `io.reactivex.functions.Function` can be specified that projects each item emitted by the source into a new value that will be used for comparison with previous projected values. Alternatively, a `io.reactivex.functions.BiPredicate` can be specified that is used as the comparator function to compare immediate predecessors with each other. + +### distinctUntilChanged example + +```java +Observable.just(1, 1, 2, 1, 2, 3, 3, 4) + .distinctUntilChanged() + .subscribe(System.out::println); + +// prints: +// 1 +// 2 +// 1 +// 2 +// 3 +// 4 +``` + +## elementAt + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/elementat.html](http://reactivex.io/documentation/operators/elementat.html) + +Emits the single item at the specified zero-based index in a sequence of emissions from a reactive source. A default item can be specified that will be emitted if the specified index is not within the sequence. + +### elementAt example + +```java +Observable source = Observable.generate(() -> 1L, (state, emitter) -> { + emitter.onNext(state); + + return state + 1L; +}).scan((product, x) -> product * x); + +Maybe element = source.elementAt(5); +element.subscribe(System.out::println); + +// prints 720 +``` + +## elementAtOrError + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/elementat.html](http://reactivex.io/documentation/operators/elementat.html) + +Emits the single item at the specified zero-based index in a sequence of emissions from a reactive source, or signals a `java.util.NoSuchElementException` if the specified index is not within the sequence. + +### elementAtOrError example + +```java +Observable source = Observable.just("Kirk", "Spock", "Chekov", "Sulu"); +Single element = source.elementAtOrError(4); + +element.subscribe( + name -> System.out.println("onSuccess will not be printed!"), + error -> System.out.println("onError: " + error)); + +// prints: +// onError: java.util.NoSuchElementException +``` + +## filter + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/filter.html](http://reactivex.io/documentation/operators/filter.html) + +Filters items emitted by a reactive source by only emitting those that satisfy a specified predicate. + +### filter example + +```java +Observable.just(1, 2, 3, 4, 5, 6) + .filter(x -> x % 2 == 0) + .subscribe(System.out::println); + +// prints: +// 2 +// 4 +// 6 +``` + +## first + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/first.html](http://reactivex.io/documentation/operators/first.html) + +Emits only the first item emitted by a reactive source, or emits the given default item if the source completes without emitting an item. This differs from [`firstElement`](#firstelement) in that this operator returns a `Single` whereas [`firstElement`](#firstelement) returns a `Maybe`. + +### first example + +```java +Observable source = Observable.just("A", "B", "C"); +Single firstOrDefault = source.first("D"); + +firstOrDefault.subscribe(System.out::println); + +// prints A +``` + +## firstElement + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/first.html](http://reactivex.io/documentation/operators/first.html) + +Emits only the first item emitted by a reactive source, or just completes if the source completes without emitting an item. This differs from [`first`](#first) in that this operator returns a `Maybe` whereas [`first`](#first) returns a `Single`. + +### firstElement example + +```java +Observable source = Observable.just("A", "B", "C"); +Maybe first = source.firstElement(); + +first.subscribe(System.out::println); + +// prints A +``` + +## firstOrError + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/first.html](http://reactivex.io/documentation/operators/first.html) + +Emits only the first item emitted by a reactive source, or signals a `java.util.NoSuchElementException` if the source completes without emitting an item. + +### firstOrError example + +```java +Observable emptySource = Observable.empty(); +Single firstOrError = emptySource.firstOrError(); + +firstOrError.subscribe( + element -> System.out.println("onSuccess will not be printed!"), + error -> System.out.println("onError: " + error)); + +// prints: +// onError: java.util.NoSuchElementException +``` + +## ignoreElement + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/ignoreelements.html](http://reactivex.io/documentation/operators/ignoreelements.html) + +Ignores the single item emitted by a `Single` or `Maybe` source, and returns a `Completable` that signals only the error or completion event from the source. + +### ignoreElement example + +```java +Single source = Single.timer(1, TimeUnit.SECONDS); +Completable completable = source.ignoreElement(); + +completable.doOnComplete(() -> System.out.println("Done!")) + .blockingAwait(); + +// prints (after 1 second): +// Done! +``` + +## ignoreElements + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/ignoreelements.html](http://reactivex.io/documentation/operators/ignoreelements.html) + +Ignores all items from the `Observable` or `Flowable` source, and returns a `Completable` that signals only the error or completion event from the source. + +### ignoreElements example + +```java +Observable source = Observable.intervalRange(1, 5, 1, 1, TimeUnit.SECONDS); +Completable completable = source.ignoreElements(); + +completable.doOnComplete(() -> System.out.println("Done!")) + .blockingAwait(); + +// prints (after 5 seconds): +// Done! +``` + +## last + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/last.html](http://reactivex.io/documentation/operators/last.html) + +Emits only the last item emitted by a reactive source, or emits the given default item if the source completes without emitting an item. This differs from [`lastElement`](#lastelement) in that this operator returns a `Single` whereas [`lastElement`](#lastelement) returns a `Maybe`. + +### last example + +```java +Observable source = Observable.just("A", "B", "C"); +Single lastOrDefault = source.last("D"); + +lastOrDefault.subscribe(System.out::println); + +// prints C +``` + +## lastElement + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/last.html](http://reactivex.io/documentation/operators/last.html) + +Emits only the last item emitted by a reactive source, or just completes if the source completes without emitting an item. This differs from [`last`](#last) in that this operator returns a `Maybe` whereas [`last`](#last) returns a `Single`. + +### lastElement example + +```java +Observable source = Observable.just("A", "B", "C"); +Maybe last = source.lastElement(); + +last.subscribe(System.out::println); + +// prints C +``` + +## lastOrError + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/last.html](http://reactivex.io/documentation/operators/last.html) + +Emits only the last item emitted by a reactive source, or signals a `java.util.NoSuchElementException` if the source completes without emitting an item. + +### lastOrError example + +```java +Observable emptySource = Observable.empty(); +Single lastOrError = emptySource.lastOrError(); + +lastOrError.subscribe( + element -> System.out.println("onSuccess will not be printed!"), + error -> System.out.println("onError: " + error)); + +// prints: +// onError: java.util.NoSuchElementException +``` + +## ofType + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/filter.html](http://reactivex.io/documentation/operators/filter.html) + +Filters items emitted by a reactive source by only emitting those of the specified type. + +### ofType example + +```java +Observable numbers = Observable.just(1, 4.0, 3, 2.71, 2f, 7); +Observable integers = numbers.ofType(Integer.class); + +integers.subscribe((Integer x) -> System.out.println(x)); + +// prints: +// 1 +// 3 +// 7 +``` + +## sample + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/sample.html](http://reactivex.io/documentation/operators/sample.html) + +Filters items emitted by a reactive source by only emitting the most recently emitted item within periodic time intervals. + +### sample example + +```java +// Diagram: +// -A----B-C-------D-----E-|--> +// -0s-----c--1s---d----2s-|--> +// -----------C---------D--|--> + +Observable source = Observable.create(emitter -> { + emitter.onNext("A"); + + Thread.sleep(500); + emitter.onNext("B"); + + Thread.sleep(200); + emitter.onNext("C"); + + Thread.sleep(800); + emitter.onNext("D"); + + Thread.sleep(600); + emitter.onNext("E"); + emitter.onComplete(); +}); + +source.subscribeOn(Schedulers.io()) + .sample(1, TimeUnit.SECONDS) + .blockingSubscribe( + item -> System.out.println("onNext: " + item), + Throwable::printStackTrace, + () -> System.out.println("onComplete")); + +// prints: +// onNext: C +// onNext: D +// onComplete +``` + +## skip + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/skip.html](http://reactivex.io/documentation/operators/skip.html) + +Drops the first *n* items emitted by a reactive source, and emits the remaining items. + +### skip example + +```java +Observable source = Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + +source.skip(4) + .subscribe(System.out::println); + +// prints: +// 5 +// 6 +// 7 +// 8 +// 9 +// 10 +``` + +## skipLast + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/skiplast.html](http://reactivex.io/documentation/operators/skiplast.html) + +Drops the last *n* items emitted by a reactive source, and emits the remaining items. + +### skipLast example + +```java +Observable source = Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + +source.skipLast(4) + .subscribe(System.out::println); + +// prints: +// 1 +// 2 +// 3 +// 4 +// 5 +// 6 +``` + +## take + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/take.html](http://reactivex.io/documentation/operators/take.html) + +Emits only the first *n* items emitted by a reactive source. + +### take example + +```java +Observable source = Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + +source.take(4) + .subscribe(System.out::println); + +// prints: +// 1 +// 2 +// 3 +// 4 +``` + +## takeLast + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/takelast.html](http://reactivex.io/documentation/operators/takelast.html) + +Emits only the last *n* items emitted by a reactive source. + +### takeLast example + +```java +Observable source = Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + +source.takeLast(4) + .subscribe(System.out::println); + +// prints: +// 7 +// 8 +// 9 +// 10 +``` + +## throttleFirst + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/sample.html](http://reactivex.io/documentation/operators/sample.html) + +Emits only the first item emitted by a reactive source during sequential time windows of a specified duration. + +### throttleFirst example + +```java +// Diagram: +// -A----B-C-------D-----E-|--> +// a---------1s +// d-------|--> +// -A--------------D-------|--> + +Observable source = Observable.create(emitter -> { + emitter.onNext("A"); + + Thread.sleep(500); + emitter.onNext("B"); + + Thread.sleep(200); + emitter.onNext("C"); + + Thread.sleep(800); + emitter.onNext("D"); + + Thread.sleep(600); + emitter.onNext("E"); + emitter.onComplete(); +}); + +source.subscribeOn(Schedulers.io()) + .throttleFirst(1, TimeUnit.SECONDS) + .blockingSubscribe( + item -> System.out.println("onNext: " + item), + Throwable::printStackTrace, + () -> System.out.println("onComplete")); + +// prints: +// onNext: A +// onNext: D +// onComplete +``` + +## throttleLast + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/sample.html](http://reactivex.io/documentation/operators/sample.html) + +Emits only the last item emitted by a reactive source during sequential time windows of a specified duration. + +### throttleLast example + +```java +// Diagram: +// -A----B-C-------D-----E-|--> +// -0s-----c--1s---d----2s-|--> +// -----------C---------D--|--> + +Observable source = Observable.create(emitter -> { + emitter.onNext("A"); + + Thread.sleep(500); + emitter.onNext("B"); + + Thread.sleep(200); + emitter.onNext("C"); + + Thread.sleep(800); + emitter.onNext("D"); + + Thread.sleep(600); + emitter.onNext("E"); + emitter.onComplete(); +}); + +source.subscribeOn(Schedulers.io()) + .throttleLast(1, TimeUnit.SECONDS) + .blockingSubscribe( + item -> System.out.println("onNext: " + item), + Throwable::printStackTrace, + () -> System.out.println("onComplete")); + +// prints: +// onNext: C +// onNext: D +// onComplete +``` + +## throttleLatest + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/sample.html](http://reactivex.io/documentation/operators/sample.html) + +Emits the next item emitted by a reactive source, then periodically emits the latest item (if any) when the specified timeout elapses between them. + +### throttleLatest example + +```java +// Diagram: +// -A----B-C-------D-----E-|--> +// -a------c--1s +// -----d----1s +// -e-|--> +// -A---------C---------D--|--> + +Observable source = Observable.create(emitter -> { + emitter.onNext("A"); + + Thread.sleep(500); + emitter.onNext("B"); + + Thread.sleep(200); + emitter.onNext("C"); + + Thread.sleep(800); + emitter.onNext("D"); + + Thread.sleep(600); + emitter.onNext("E"); + emitter.onComplete(); +}); + +source.subscribeOn(Schedulers.io()) + .throttleLatest(1, TimeUnit.SECONDS) + .blockingSubscribe( + item -> System.out.println("onNext: " + item), + Throwable::printStackTrace, + () -> System.out.println("onComplete")); + +// prints: +// onNext: A +// onNext: C +// onNext: D +// onComplete +``` + +## throttleWithTimeout + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/debounce.html](http://reactivex.io/documentation/operators/debounce.html) + +> Alias to [debounce](#debounce) + +Drops items emitted by a reactive source that are followed by newer items before the given timeout value expires. The timer resets on each emission. + +### throttleWithTimeout example + +```java +// Diagram: +// -A--------------B----C-D-------------------E-|----> +// a---------1s +// b---------1s +// c---------1s +// d---------1s +// e-|----> +// -----------A---------------------D-----------E-|--> + +Observable source = Observable.create(emitter -> { + emitter.onNext("A"); + + Thread.sleep(1_500); + emitter.onNext("B"); + + Thread.sleep(500); + emitter.onNext("C"); + + Thread.sleep(250); + emitter.onNext("D"); + + Thread.sleep(2_000); + emitter.onNext("E"); + emitter.onComplete(); +}); + +source.subscribeOn(Schedulers.io()) + .throttleWithTimeout(1, TimeUnit.SECONDS) + .blockingSubscribe( + item -> System.out.println("onNext: " + item), + Throwable::printStackTrace, + () -> System.out.println("onComplete")); + +// prints: +// onNext: A +// onNext: D +// onNext: E +// onComplete +``` + +## timeout + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/timeout.html](http://reactivex.io/documentation/operators/timeout.html) + +Emits the items from the `Observable` or `Flowable` source, but terminates with a `java.util.concurrent.TimeoutException` if the next item is not emitted within the specified timeout duration starting from the previous item. For `Maybe`, `Single` and `Completable` the specified timeout duration specifies the maximum time to wait for a success or completion event to arrive. If the `Maybe`, `Single` or `Completable` does not complete within the given time a `java.util.concurrent.TimeoutException` will be emitted. + +### timeout example + +```java +// Diagram: +// -A-------B---C-----------D-|--> +// a---------1s +// b---------1s +// c---------1s +// -A-------B---C---------X------> + +Observable source = Observable.create(emitter -> { + emitter.onNext("A"); + + Thread.sleep(800); + emitter.onNext("B"); + + Thread.sleep(400); + emitter.onNext("C"); + + Thread.sleep(1200); + emitter.onNext("D"); + emitter.onComplete(); +}); + +source.timeout(1, TimeUnit.SECONDS) + .subscribe( + item -> System.out.println("onNext: " + item), + error -> System.out.println("onError: " + error), + () -> System.out.println("onComplete will not be printed!")); + +// prints: +// onNext: A +// onNext: B +// onNext: C +// onError: java.util.concurrent.TimeoutException: The source did not signal an event for 1 seconds and has been terminated. +``` diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md new file mode 100644 index 0000000000..69b69a8ca6 --- /dev/null +++ b/docs/Getting-Started.md @@ -0,0 +1,129 @@ +## Getting Binaries + +You can find binaries and dependency information for Maven, Ivy, Gradle, SBT, and others at [http://search.maven.org](https://search.maven.org/search?q=g:io.reactivex.rxjava3%20AND%20rxjava). + +Example for Maven: + +```xml + + io.reactivex.rxjava3 + rxjava + 3.0.4 + +``` +and for Ivy: + +```xml + +``` + +and for SBT: + +```scala +libraryDependencies += "io.reactivex" %% "rxscala" % "0.26.5" + +libraryDependencies += "io.reactivex.rxjava3" % "rxjava" % "3.0.4" +``` + +and for Gradle: +```groovy +implementation 'io.reactivex.rxjava3:rxjava:3.0.4' +``` + +If you need to download the jars instead of using a build system, create a Maven `pom` file like this with the desired version: + +```xml + + + 4.0.0 + io.reactivex.rxjava3 + rxjava + 3.0.4 + RxJava + Reactive Extensions for Java + https://github.com/ReactiveX/RxJava + + + io.reactivex.rxjava3 + rxjava + 3.0.4 + + + +``` + +Then execute: + +``` +$ mvn -f download-rxjava-pom.xml dependency:copy-dependencies +``` + +That command downloads `rxjava-*.jar` and its dependencies into `./target/dependency/`. + +You need Java 6 or later. + +### Snapshots + +Snapshots after May 1st, 2021 are available via https://oss.sonatype.org/content/repositories/snapshots/io/reactivex/rxjava3/rxjava/ + +```groovy +repositories { + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } +} + +dependencies { + implementation 'io.reactivex.rxjava3:rxjava:3.0.0-SNAPSHOT' +} +``` + +JavaDoc snapshots are available at http://reactivex.io/RxJava/3.x/javadoc/snapshot + + +## Building + +To check out and build the RxJava source, issue the following commands: + +``` +$ git clone git@github.com:ReactiveX/RxJava.git +$ cd RxJava/ +$ ./gradlew build +``` + +To do a clean build, issue the following command: + +``` +$ ./gradlew clean build +``` + +A build should look similar to this: + +``` +$ ./gradlew build +:rxjava:compileJava +:rxjava:processResources UP-TO-DATE +:rxjava:classes +:rxjava:jar +:rxjava:sourcesJar +:rxjava:signArchives SKIPPED +:rxjava:assemble +:rxjava:licenseMain UP-TO-DATE +:rxjava:licenseTest UP-TO-DATE +:rxjava:compileTestJava +:rxjava:processTestResources UP-TO-DATE +:rxjava:testClasses +:rxjava:test +:rxjava:check +:rxjava:build + +BUILD SUCCESSFUL + +Total time: 30.758 secs +``` + +On a clean build you will see the unit tests run. They will look something like this: + +``` +> Building > :rxjava:test > 91 tests completed +``` diff --git a/docs/Home.md b/docs/Home.md new file mode 100644 index 0000000000..474c8edc77 --- /dev/null +++ b/docs/Home.md @@ -0,0 +1,24 @@ +RxJava is a Java VM implementation of [ReactiveX (Reactive Extensions)](https://reactivex.io): a library for composing asynchronous and event-based programs by using observable sequences. + +For more information about ReactiveX, see the [Introduction to ReactiveX](http://reactivex.io/intro.html) page. + +### RxJava is Lightweight + +RxJava tries to be very lightweight. It is implemented as a single JAR that is focused on just the Observable abstraction and related higher-order functions. + +### RxJava is a Polyglot Implementation + +RxJava supports Java 6 or higher and JVM-based languages such as [Groovy](https://github.com/ReactiveX/RxGroovy), [Clojure](https://github.com/ReactiveX/RxClojure), [JRuby](https://github.com/ReactiveX/RxJRuby), [Kotlin](https://github.com/ReactiveX/RxKotlin) and [Scala](https://github.com/ReactiveX/RxScala). + +RxJava is meant for a more polyglot environment than just Java/Scala, and it is being designed to respect the idioms of each JVM-based language. (This is something we’re still working on.) + +### RxJava Libraries + +The following external libraries can work with RxJava: + +* [Hystrix](https://github.com/Netflix/Hystrix/wiki/How-To-Use#wiki-Reactive-Execution) latency and fault tolerance bulkheading library. +* [Camel RX](http://camel.apache.org/rx.html) provides an easy way to reuse any of the [Apache Camel components, protocols, transports and data formats](http://camel.apache.org/components.html) with the RxJava API +* [rxjava-http-tail](https://github.com/myfreeweb/rxjava-http-tail) allows you to follow logs over HTTP, like `tail -f` +* [mod-rxvertx - Extension for VertX](https://github.com/vert-x/mod-rxvertx) that provides support for Reactive Extensions (RX) using the RxJava library +* [rxjava-jdbc](https://github.com/davidmoten/rxjava-jdbc) - use RxJava with jdbc connections to stream ResultSets and do functional composition of statements +* [rtree](https://github.com/davidmoten/rtree) - immutable in-memory R-tree and R*-tree with RxJava api including backpressure \ No newline at end of file diff --git a/docs/How-To-Use-RxJava.md b/docs/How-To-Use-RxJava.md new file mode 100644 index 0000000000..41a46bb75d --- /dev/null +++ b/docs/How-To-Use-RxJava.md @@ -0,0 +1,405 @@ +# Hello World! + +The following sample implementations of “Hello World” in Java, Groovy, Clojure, and Scala create an Observable from a list of Strings, and then subscribe to this Observable with a method that prints “Hello _String_!” for each string emitted by the Observable. + +You can find additional code examples in the `/src/examples` folders of each [language adaptor](https://github.com/ReactiveX/): + +* [RxGroovy examples](https://github.com/ReactiveX/RxGroovy/tree/1.x/src/examples/groovy/rx/lang/groovy/examples) +* [RxClojure examples](https://github.com/ReactiveX/RxClojure/tree/0.x/src/examples/clojure/rx/lang/clojure/examples) +* [RxScala examples](https://github.com/ReactiveX/RxScala/tree/0.x/examples/src/main/scala) + +### Java + +```java +public static void hello(String... args) { + Flowable.fromArray(args).subscribe(s -> System.out.println("Hello " + s + "!")); +} +``` + +If your platform doesn't support Java 8 lambdas (yet), you have to create an inner class of ```Consumer``` manually: +```java +public static void hello(String... args) { + Flowable.fromArray(args).subscribe(new Consumer() { + @Override + public void accept(String s) { + System.out.println("Hello " + s + "!"); + } + }); +} +``` + +```java +hello("Ben", "George"); +Hello Ben! +Hello George! +``` + +### Groovy + +```groovy +def hello(String[] names) { + Observable.from(names).subscribe { println "Hello ${it}!" } +} +``` + +```groovy +hello("Ben", "George") +Hello Ben! +Hello George! +``` + +### Clojure + +```clojure +(defn hello + [&rest] + (-> (Observable/from &rest) + (.subscribe #(println (str "Hello " % "!"))))) +``` + +``` +(hello ["Ben" "George"]) +Hello Ben! +Hello George! +``` +### Scala + +```scala +import rx.lang.scala.Observable + +def hello(names: String*) { + Observable.from(names) subscribe { n => + println(s"Hello $n!") + } +} +``` + +```scala +hello("Ben", "George") +Hello Ben! +Hello George! +``` + +# How to Design Using RxJava + +To use RxJava you create Observables (which emit data items), transform those Observables in various ways to get the precise data items that interest you (by using Observable operators), and then observe and react to these sequences of interesting items (by implementing Observers or Subscribers and then subscribing them to the resulting transformed Observables). + +## Creating Observables + +To create an Observable, you can either implement the Observable's behavior manually by passing a function to [`create( )`](http://reactivex.io/documentation/operators/create.html) that exhibits Observable behavior, or you can convert an existing data structure into an Observable by using [some of the Observable operators that are designed for this purpose](Creating-Observables). + +### Creating an Observable from an Existing Data Structure + +You use the Observable [`just( )`](http://reactivex.io/documentation/operators/just.html) and [`from( )`](http://reactivex.io/documentation/operators/from.html) methods to convert objects, lists, or arrays of objects into Observables that emit those objects: + +```groovy +Observable o = Observable.from("a", "b", "c"); + +def list = [5, 6, 7, 8] +Observable o2 = Observable.from(list); + +Observable o3 = Observable.just("one object"); +``` + +These converted Observables will synchronously invoke the [`onNext( )`](Observable#onnext-oncompleted-and-onerror) method of any subscriber that subscribes to them, for each item to be emitted by the Observable, and will then invoke the subscriber’s [`onCompleted( )`](Observable#onnext-oncompleted-and-onerror) method. + +### Creating an Observable via the `create( )` method + +You can implement asynchronous i/o, computational operations, or even “infinite” streams of data by designing your own Observable and implementing it with the [`create( )`](http://reactivex.io/documentation/operators/create.html) method. + +#### Synchronous Observable Example + +```groovy +/** + * This example shows a custom Observable that blocks + * when subscribed to (does not spawn an extra thread). + */ +def customObservableBlocking() { + return Observable.create { aSubscriber -> + 50.times { i -> + if (!aSubscriber.unsubscribed) { + aSubscriber.onNext("value_${i}") + } + } + // after sending all values we complete the sequence + if (!aSubscriber.unsubscribed) { + aSubscriber.onCompleted() + } + } +} + +// To see output: +customObservableBlocking().subscribe { println(it) } +``` + +#### Asynchronous Observable Example + +The following example uses Groovy to create an Observable that emits 75 strings. + +It is written verbosely, with static typing and implementation of the `Func1` anonymous inner class, to make the example more clear: + +```groovy +/** + * This example shows a custom Observable that does not block + * when subscribed to as it spawns a separate thread. + */ +def customObservableNonBlocking() { + return Observable.create({ subscriber -> + Thread.start { + for (i in 0..<75) { + if (subscriber.unsubscribed) { + return + } + subscriber.onNext("value_${i}") + } + // after sending all values we complete the sequence + if (!subscriber.unsubscribed) { + subscriber.onCompleted() + } + } + } as Observable.OnSubscribe) +} + +// To see output: +customObservableNonBlocking().subscribe { println(it) } +``` + +Here is the same code in Clojure that uses a Future (instead of raw thread) and is implemented more consisely: + +```clojure +(defn customObservableNonBlocking [] + "This example shows a custom Observable that does not block + when subscribed to as it spawns a separate thread. + + returns Observable" + (Observable/create + (fn [subscriber] + (let [f (future + (doseq [x (range 50)] (-> subscriber (.onNext (str "value_" x)))) + ; after sending all values we complete the sequence + (-> subscriber .onCompleted)) + )) + )) +``` + +```clojure +; To see output +(.subscribe (customObservableNonBlocking) #(println %)) +``` + +Here is an example that fetches articles from Wikipedia and invokes onNext with each one: + +```clojure +(defn fetchWikipediaArticleAsynchronously [wikipediaArticleNames] + "Fetch a list of Wikipedia articles asynchronously. + + return Observable of HTML" + (Observable/create + (fn [subscriber] + (let [f (future + (doseq [articleName wikipediaArticleNames] + (-> subscriber (.onNext (http/get (str "http://en.wikipedia.org/wiki/" articleName))))) + ; after sending response to onnext we complete the sequence + (-> subscriber .onCompleted)) + )))) +``` + +```clojure +(-> (fetchWikipediaArticleAsynchronously ["Tiger" "Elephant"]) + (.subscribe #(println "--- Article ---\n" (subs (:body %) 0 125) "..."))) +``` + +Back to Groovy, the same Wikipedia functionality but using closures instead of anonymous inner classes: + +```groovy +/* + * Fetch a list of Wikipedia articles asynchronously. + */ +def fetchWikipediaArticleAsynchronously(String... wikipediaArticleNames) { + return Observable.create { subscriber -> + Thread.start { + for (articleName in wikipediaArticleNames) { + if (subscriber.unsubscribed) { + return + } + subscriber.onNext(new URL("http://en.wikipedia.org/wiki/${articleName}").text) + } + if (!subscriber.unsubscribed) { + subscriber.onCompleted() + } + } + return subscriber + } +} + +fetchWikipediaArticleAsynchronously("Tiger", "Elephant") + .subscribe { println "--- Article ---\n${it.substring(0, 125)}" } +``` + +Results: + +```text +--- Article --- + + + +Tiger - Wikipedia, the free encyclopedia ... +--- Article --- + + + +Elephant - Wikipedia, the free encyclopedia</tit ... +``` + +Note that all of the above examples ignore error handling, for brevity. See below for examples that include error handling. + +More information can be found on the [[Observable]] and [[Creating Observables|Creating-Observables]] pages. + +## Transforming Observables with Operators + +RxJava allows you to chain _operators_ together to transform and compose Observables. + +The following example, in Groovy, uses a previously defined, asynchronous Observable that emits 75 items, skips over the first 10 of these ([`skip(10)`](http://reactivex.io/documentation/operators/skip.html)), then takes the next 5 ([`take(5)`](http://reactivex.io/documentation/operators/take.html)), and transforms them ([`map(...)`](http://reactivex.io/documentation/operators/map.html)) before subscribing and printing the items: + +```groovy +/** + * Asynchronously calls 'customObservableNonBlocking' and defines + * a chain of operators to apply to the callback sequence. + */ +def simpleComposition() { + customObservableNonBlocking().skip(10).take(5) + .map({ stringValue -> return stringValue + "_xform"}) + .subscribe({ println "onNext => " + it}) +} +``` + +This results in: + +```text +onNext => value_10_xform +onNext => value_11_xform +onNext => value_12_xform +onNext => value_13_xform +onNext => value_14_xform +``` + +Here is a marble diagram that illustrates this transformation: + +<img src="https://github.com/ReactiveX/RxJava/wiki/images/rx-operators/Composition.1.v3.png" width="640" height="536" /> + +This next example, in Clojure, consumes three asynchronous Observables, including a dependency from one to another, and emits a single response item by combining the items emitted by each of the three Observables with the [`zip`](http://reactivex.io/documentation/operators/zip.html) operator and then transforming the result with [`map`](http://reactivex.io/documentation/operators/map.html): + +```clojure +(defn getVideoForUser [userId videoId] + "Get video metadata for a given userId + - video metadata + - video bookmark position + - user data + return Observable<Map>" + (let [user-observable (-> (getUser userId) + (.map (fn [user] {:user-name (:name user) :language (:preferred-language user)}))) + bookmark-observable (-> (getVideoBookmark userId videoId) + (.map (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] + (getVideoMetadata videoId (:language user-map)))))] + ; now combine 3 observables using zip + (-> (Observable/zip bookmark-observable video-metadata-observable user-observable + (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] + {:video-id videoId + :video-metadata (:metadata-map data) + :user-id userId + :language (:language (:user-map data)) + :bookmark (:viewed-position (:bookmark-map data)) + }))))) +``` + +The response looks like this: + +```clojure +{:video-id 78965, + :video-metadata {:video-id 78965, :title House of Cards: Episode 1, + :director David Fincher, :duration 3365}, + :user-id 12345, :language es-us, :bookmark 0} +``` + +And here is a marble diagram that illustrates how that code produces that response: + +<img src="https://github.com/ReactiveX/RxJava/wiki/images/rx-operators/Composition.2.v3.png" width="640" height="742" /> + +The following example, in Groovy, comes from [Ben Christensen’s QCon presentation on the evolution of the Netflix API](https://speakerdeck.com/benjchristensen/evolution-of-the-netflix-api-qcon-sf-2013). It combines two Observables with the [`merge`](http://reactivex.io/documentation/operators/merge.html) operator, then uses the [`reduce`](http://reactivex.io/documentation/operators/reduce.html) operator to construct a single item out of the resulting sequence, then transforms that item with [`map`](http://reactivex.io/documentation/operators/map.html) before emitting it: + +```groovy +public Observable getVideoSummary(APIVideo video) { + def seed = [id:video.id, title:video.getTitle()]; + def bookmarkObservable = getBookmark(video); + def artworkObservable = getArtworkImageUrl(video); + return( Observable.merge(bookmarkObservable, artworkObservable) + .reduce(seed, { aggregate, current -> aggregate << current }) + .map({ [(video.id.toString() : it] })) +} +``` + +And here is a marble diagram that illustrates how that code uses the [`reduce`](http://reactivex.io/documentation/operators/reduce.html) operator to bring the results from multiple Observables together in one structure: + +<img src="https://github.com/ReactiveX/RxJava/wiki/images/rx-operators/Composition.3.v3.png" width="640" height="640" /> + +## Error Handling + +Here is a version of the Wikipedia example from above revised to include error handling: + +```groovy +/* + * Fetch a list of Wikipedia articles asynchronously, with error handling. + */ +def fetchWikipediaArticleAsynchronouslyWithErrorHandling(String... wikipediaArticleNames) { + return Observable.create({ subscriber -> + Thread.start { + try { + for (articleName in wikipediaArticleNames) { + if (true == subscriber.isUnsubscribed()) { + return; + } + subscriber.onNext(new URL("http://en.wikipedia.org/wiki/"+articleName).getText()); + } + if (false == subscriber.isUnsubscribed()) { + subscriber.onCompleted(); + } + } catch(Throwable t) { + if (false == subscriber.isUnsubscribed()) { + subscriber.onError(t); + } + } + return (subscriber); + } + }); +} +``` + +Notice how it now invokes [`onError(Throwable t)`](Observable#onnext-oncompleted-and-onerror) if an error occurs and note that the following code passes [`subscribe()`](http://reactivex.io/documentation/operators/subscribe.html) a second method that handles `onError`: + +```groovy +fetchWikipediaArticleAsynchronouslyWithErrorHandling("Tiger", "NonExistentTitle", "Elephant") + .subscribe( + { println "--- Article ---\n" + it.substring(0, 125) }, + { println "--- Error ---\n" + it.getMessage() }) +``` + +See the [Error-Handling-Operators](Error-Handling-Operators) page for more information on specialized error handling techniques in RxJava, including methods like [`onErrorResumeNext()`](http://reactivex.io/documentation/operators/catch.html) and [`onErrorReturn()`](http://reactivex.io/documentation/operators/catch.html) that allow Observables to continue with fallbacks in the event that they encounter errors. + +Here is an example of how you can use such a method to pass along custom information about any exceptions you encounter. Imagine you have an Observable or cascade of Observables — `myObservable` — and you want to intercept any exceptions that would normally pass through to an Subscriber’s `onError` method, replacing these with a customized Throwable of your own design. You could do this by modifying `myObservable` with the [`onErrorResumeNext()`](http://reactivex.io/documentation/operators/catch.html) method, and passing into that method an Observable that calls `onError` with your customized Throwable (a utility method called [`error()`](http://reactivex.io/documentation/operators/empty-never-throw.html) will generate such an Observable for you): + +```groovy +myModifiedObservable = myObservable.onErrorResumeNext({ t -> + Throwable myThrowable = myCustomizedThrowableCreator(t); + return (Observable.error(myThrowable)); +}); +``` diff --git a/docs/How-to-Contribute.md b/docs/How-to-Contribute.md new file mode 100644 index 0000000000..d7c4a3ceb7 --- /dev/null +++ b/docs/How-to-Contribute.md @@ -0,0 +1,41 @@ +RxJava is still a work in progress and has a long list of work documented in the [Issues](https://github.com/ReactiveX/RxJava/issues). + + +If you wish to contribute we would ask that you: +- read [Rx Design Guidelines](http://blogs.msdn.com/b/rxteam/archive/2010/10/28/rx-design-guidelines.aspx) +- review existing code and comply with existing patterns and idioms +- include unit tests +- stick to Rx contracts as defined by the Rx.Net implementation when porting operators (each issue attempts to reference the correct documentation from MSDN) + +Information about licensing can be found at: [CONTRIBUTING](https://github.com/ReactiveX/RxJava/blob/2.x/CONTRIBUTING.md). + +## How to import the project into Eclipse +Two options below: + +### Import as Eclipse project + + ./gradlew eclipse + +In Eclipse +* choose File - Import - General - Existing Projects into Workspace +* Browse to RxJava folder +* click Finish. +* Right click on the project in Package Explorer, select Properties - Java Compiler - Errors/Warnings - click Enable project specific settings. +* Still in Errors/Warnings, go to Deprecated and restricted API and set Forbidden reference (access-rules) to Warning. + +### Import as Gradle project + +You need the Gradle plugin for Eclipse installed. + +In Eclipse +* choose File - Import - Gradle - Gradle Project. +* Browse to RxJava folder +* click Build Model +* select the project +* click Finish + + + + + + diff --git a/docs/Implementing-Your-Own-Operators.md b/docs/Implementing-Your-Own-Operators.md new file mode 100644 index 0000000000..9e4a165f8b --- /dev/null +++ b/docs/Implementing-Your-Own-Operators.md @@ -0,0 +1,114 @@ +You can implement your own Observable operators. This page shows you how. + +If your operator is designed to *originate* an Observable, rather than to transform or react to a source Observable, use the [`create( )`](http://reactivex.io/documentation/operators/create.html) method rather than trying to implement `Observable` manually. Otherwise, you can create a custom operator by following the instructions on this page. + +If your operator is designed to act on the individual items emitted by a source Observable, follow the instructions under [_Sequence Operators_](Implementing-Your-Own-Operators#sequence-operators) below. If your operator is designed to transform the source Observable as a whole (for instance, by applying a particular set of existing RxJava operators to it) follow the instructions under [_Transformational Operators_](Implementing-Your-Own-Operators#transformational-operators) below. + +(**Note:** in Xtend, a Groovy-like language, you can implement your operators as _extension methods_ and can thereby chain them directly without using the methods described on this page. See [RxJava and Xtend](http://mnmlst-dvlpr.blogspot.de/2014/07/rxjava-and-xtend.html) for details.) + +# Sequence Operators + +The following example shows how you can use the `lift( )` operator to chain your custom operator (in this example: `myOperator`) alongside standard RxJava operators like `ofType` and `map`: +```groovy +fooObservable = barObservable.ofType(Integer).map({it*2}).lift(new myOperator<T>()).map({"transformed by myOperator: " + it}); +``` +The following section shows how you form the scaffolding of your operator so that it will work correctly with `lift( )`. + +## Implementing Your Operator + +Define your operator as a public class that implements the [`Operator`](http://reactivex.io/RxJava/javadoc/rx/Observable.Operator.html) interface, like so: +```java +public class myOperator<T> implements Operator<T> { + public myOperator( /* any necessary params here */ ) { + /* any necessary initialization here */ + } + + @Override + public Subscriber<? super T> call(final Subscriber<? super T> s) { + return new Subscriber<t>(s) { + @Override + public void onCompleted() { + /* add your own onCompleted behavior here, or just pass the completed notification through: */ + if(!s.isUnsubscribed()) { + s.onCompleted(); + } + } + + @Override + public void onError(Throwable t) { + /* add your own onError behavior here, or just pass the error notification through: */ + if(!s.isUnsubscribed()) { + s.onError(t); + } + } + + @Override + public void onNext(T item) { + /* this example performs some sort of operation on each incoming item and emits the results */ + if(!s.isUnsubscribed()) { + transformedItem = myOperatorTransformOperation(item); + s.onNext(transformedItem); + } + } + }; + } +} +``` + +# Transformational Operators + +The following example shows how you can use the `compose( )` operator to chain your custom operator (in this example, an operator called `myTransformer` that transforms an Observable that emits Integers into one that emits Strings) alongside standard RxJava operators like `ofType` and `map`: +```groovy +fooObservable = barObservable.ofType(Integer).map({it*2}).compose(new myTransformer<Integer,String>()).map({"transformed by myOperator: " + it}); +``` +The following section shows how you form the scaffolding of your operator so that it will work correctly with `compose( )`. + +## Implementing Your Transformer + +Define your transforming function as a public class that implements the [`Transformer`](http://reactivex.io/RxJava/javadoc/rx/Observable.Transformer.html) interface, like so: + +````java +public class myTransformer<Integer,String> implements Transformer<Integer,String> { + public myTransformer( /* any necessary params here */ ) { + /* any necessary initialization here */ + } + + @Override + public Observable<String> call(Observable<Integer> source) { + /* + * this simple example Transformer applies map() to the source Observable + * in order to transform the "source" observable from one that emits + * integers to one that emits string representations of those integers. + */ + return source.map( new Func1<Integer,String>() { + @Override + public String call(Integer t1) { + return String.valueOf(t1); + } + } ); + } +} +```` + +## See also + +* [“Don’t break the chain: use RxJava’s compose() operator”](http://blog.danlew.net/2015/03/02/dont-break-the-chain/) by Dan Lew + +# Other Considerations + +* Your sequence operator may want to check [its Subscriber’s `isUnsubscribed( )` status](Observable#unsubscribing) before it emits any item to (or sends any notification to) the Subscriber. There’s no need to waste time generating items that no Subscriber is interested in seeing. +* Take care that your sequence operator obeys the core tenets of the Observable contract: + * It may call a Subscriber’s [`onNext( )`](Observable#onnext-oncompleted-and-onerror) method any number of times, but these calls must be non-overlapping. + * It may call either a Subscriber’s [`onCompleted( )`](Observable#onnext-oncompleted-and-onerror) or [`onError( )`](Observable#onnext-oncompleted-and-onerror) method, but not both, exactly once, and it may not subsequently call a Subscriber’s [`onNext( )`](Observable#onnext-oncompleted-and-onerror) method. + * If you are unable to guarantee that your operator conforms to the above two tenets, you can add the [`serialize( )`](Observable-Utility-Operators#serialize) operator to it, which will force the correct behavior. +* Keep an eye on [Issue #1962](https://github.com/ReactiveX/RxJava/issues/1962) — there are plans to create a test scaffold that you can use to write tests which verify that your new operator conforms to the Observable contract. +* Do not block within your operator. +* When possible, you should compose new operators by combining existing operators, rather than implementing them with new code. RxJava itself does this with some of its standard operators, for example: + * [`first( )`](http://reactivex.io/documentation/operators/first.html) is defined as <tt>[take(1)](http://reactivex.io/documentation/operators/take.html).[single( )](http://reactivex.io/documentation/operators/first.html)</tt> + * [`ignoreElements( )`](http://reactivex.io/documentation/operators/ignoreelements.html) is defined as <tt>[filter(alwaysFalse( ))](http://reactivex.io/documentation/operators/filter.html)</tt> + * [`reduce(a)`](http://reactivex.io/documentation/operators/reduce.html) is defined as <tt>[scan(a)](http://reactivex.io/documentation/operators/scan.html).[last( )](http://reactivex.io/documentation/operators/last.html)</tt> +* If your operator uses functions or lambdas that are passed in as parameters (predicates, for instance), note that these may be sources of exceptions, and be prepared to catch these and notify subscribers via `onError( )` calls. + * Some exceptions are considered “fatal” and for them there’s no point in trying to call `onError( )` because that will either be futile or will just compound the problem. You can use the `Exceptions.throwIfFatal(throwable)` method to filter out such fatal exceptions and rethrow them rather than try to notify about them. +* In general, notify subscribers of error conditions immediately, rather than making an effort to emit more items first. +* Be aware that “<code>null</code>” is a valid item that may be emitted by an Observable. A frequent source of bugs is to test some variable meant to hold an emitted item against <code>null</code> as a substitute for testing whether or not an item was emitted. An emission of “<code>null</code>” is still an emission and is not the same as not emitting anything. +* It can be tricky to make your operator behave well in *backpressure* scenarios. See [Advanced RxJava](http://akarnokd.blogspot.hu/), a blog from Dávid Karnok, for a good discussion of the factors at play and how to deal with them. \ No newline at end of file diff --git a/docs/Implementing-custom-operators-(draft).md b/docs/Implementing-custom-operators-(draft).md new file mode 100644 index 0000000000..a95b6968f0 --- /dev/null +++ b/docs/Implementing-custom-operators-(draft).md @@ -0,0 +1,508 @@ +# Introduction + +RxJava features over 100 operators to support the most common reactive dataflow patterns. Generally, there exist a combination of operators, typically `flatMap`, `defer` and `publish`, that allow composing less common patterns with standard guarantees. When you have an uncommon pattern and you can't seem to find the right operators, try asking about it on our issue list (or Stackoverflow) first. + +If none of this applies to your use case, you may want to implement a custom operator. Be warned that **writing operators is hard**: when one writes an operator, the `Observable` **protocol**, **unsubscription**, **backpressure** and **concurrency** have to be taken into account and adhered to the letter. + +*Note that this page uses Java 8 syntax for brevity.* + +# Considerations + +## Observable protocol + +The `Observable` protocol states that you have to call the `Observer` methods, `onNext`, `onError` and `onCompleted` in a sequential manner. In other words, these can't be called concurrently and have to be **serialized**. The `SerializedObserver` and `SerializedSubscriber` wrappers help you with these. Note that there are cases where this serialization has to happen. + +In addition, there is an expected pattern of method calls on `Observer`: + +``` +onNext* (onError | onCompleted)? +``` + +A custom operator has to honor this pattern on its push side as well. For example, if your operator turns an `onNext` into an `onError`, the upstream has to be stopped and no further methods can be called on the dowstream. + +## Unsubscription + +The basic `Observer` method has no direct means to signal to the upstream source to stop emitting events. One either has to get the `Subscription` that the `Observable.subscribe(Observer<T>)` returns **and** be asynchronous itself. + +This shortcoming was resolved by introducing the `Subscriber` class that implements the `Subscription` interface. The interface allows detecting if a `Subscriber` is no longer interested in the events. + +```java +interface Subscription { + boolean isUnsubscribed(); + + void unsubscribe(); +} +``` + +In an operator, this allows active checking of the `Subscriber` state before emitting an event. + +In some cases, one needs to react to the child unsubscribing immediately and not just before an emission. To support this case, the `Subscriber` class has an `add(Subscription)` method that let's the operator register `Subscription`s of its own which get unsubscribed when the downstream calls `Subscriber.unsubscribe()`. + +```java +InputStream in = ... + +child.add(Subscriptions.create(() -> { + try { + in.close(); + } catch (IOException ex) { + RxJavaHooks.onError(ex); + } +})); +``` + +## Backpressure + +The name of this feature is often misinterpreted. It is about telling the upstream how many `onNext` events the downstream is ready to receive. For example, if the downstream requests 5, the upstream can only call `onNext` 5 times. If the upstream can't produce 5 elements but 3, it should deliver that 3 element followed by an `onError` or `onCompleted` (depending on the operator's purpose). The requests are cumulative in the sense that if the downstream requests 5 and then 2, there is going to be 7 requests outstanding. + +Backpressure handling adds a great deal of complexity to most operators: one has to track how many elements the downstream requested, how many have been delivered (by usually subtracting from the request amount) and sometimes how many elements are still available (but can't be delivered without requests). In addition, the downstream can request from any thread and is not required to happen on the common thread where otherwise the `onXXX` methods are called. + +The backpressure 'channel' is established between the upstream and downstream via the `Producer` interface: + +```java +interface Producer { + void request(long n); +} +``` + +When an upstream supports backpressure, it will call the `Subscriber.setProducer(Producer)` method on its downstream `Subscriber` with the implementation of this interface. The downstream then can respond with `Long.MAX_VALUE` to start an unbounded streaming (effectively no backpressure between the immediate upstream and downstream) or any other positive value. A request amount of zero should be ignored. + +Protocol-vise, there is no strict time when a producer can be set and it may never appear. Operators have to be ready to deal with this situation and assume the upstream runs in unbounded mode (as if `Long.MAX_VALUE` was requested). + +Often, operators may implement `Producer` and `Subscription` in a single class to handle both requests and unsubscriptions from the downstream: + +```java +final class MyEmitter implements Producer, Subscription { + final Subscriber<Integer> subscriber; + + public MyEmitter(Subscriber<Integer> subscriber) { + this.subscriber = subscriber; + } + + @Override + public void request(long n) { + if (n > 0) { + subscriber.onCompleted(); + } + } + + @Override + public void unsubscribe() { + System.out.println("Unsubscribed"); + } + + @Override + public boolean isUnsubscribed() { + return true; + } +} + +MyEmitter emitter = new MyEmitter(child); + +child.add(emitter); +child.setProducer(emitter); +``` + +Unfortunately, you can't implement `Producer` on a `Subscriber` because of an API oversight: `Subscriber` has a protected final `request(long n)` method to perform **deferred requesting** (store and accumulate the local request amounts until `setProducer` is called). + +## Concurrency + +When writing operators, we mostly have to deal with concurrency via the standard Java concurrency primitives: `AtomicXXX` classes, volatile variables, `Queue`s, mutual exclusion, Executors, etc. + +### RxJava tools + +RxJava has a few support classes and utilities that let's one deal with concurrency inside operators. + +The first one, `BackpressureUtils` deals with managing the cumulative requested and produced element counts for an operator. Its `getAndAddRequested()` method takes an `AtomicLong`, accumulates request amounts atomically and makes sure they don't overflow `Long.MAX_VALUE`. Its pair `produced()` subtracts the amount operators have produced, thus when both are in play, the given `AtomicLong` holds the current outstanding request amount for the downstream. + +Operators sometimes have to switch between multiple sources. If a previous source didn't fulfill all its requested amount, the new source has to start with that unfulfilled amount. Otherwise as the downstream didn't receive the requested amount (and no terminal event either), it can't know when to request more. If this switch happens at an `Observable` boundary (think `concat`), the `ProducerArbiter` helps managing the change. + +If there is only one item to emit eventually, the `SingleProducer` and `SingleDelayedProducer` help work out the backpressure handling: + +```java +child.setProducer(new SingleProducer<>(child, 1)); + +// or + +SingleDelayedProducer<Integer> p = new SingleDelayedProducer<>(child); + +child.add(p); +child.setProducer(p); + +p.setValue(2); +``` + +### The queue-drain approach + +Usually, one has to serialize calls to the `onXXX` methods so only one thread at a time is in any of them. The first thought, namely using `synchronized` blocks, is forbidden. It may cause deadlocks and unnecessary thread blocking. + +Most operators, however, can use a non-blocking approach called queue-drain. It works by posting the element to be emitted (or work to be performed) onto a **queue** then atomically increments a counter. If the value before the increment was zero, it means the current thread won the right to emit the contents of the queue. Once the queue is **drained**, the counter is decremented until zero and the thread continues with other activities. + +In code: + +```java +final AtomicInteger counter = new AtomicInteger(); +final Queue<T> queue = new ConcurrentLinkedQueue<>(); + +public void onNext(T t) { + queue.offer(t); + drain(); +} + +void drain() { + if (counter.getAndIncrement() == 0) { + do { + t = queue.poll(); + child.onNext(t); + } while (counter.decrementAndGet() != 0); + } +} +``` + +Often, the when the downstream requests some amount, that should also trigger a similar drain() call: + +```java + +final AtomicLong requested = new AtomicLong(); + +@Override +public void request(long n) { + if (n > 0) { + BackpressureUtils.getAndAddRequested(requested, n); + drain(); + } +} +``` + +Many operators do more than just draining the queue and emitting its content: they have to coordinate with the downstream to emit as many items from the queue as the downstream requested. + +For example, if one writes an operator that is unbounded-in but honors the requests of the downstream, the following `drain` pattern will do the job: + +```java +// downstream's consumer +final Subscriber<? super T> child; +// temporary storage for values +final Queue<T> queue; +// mutual exclusion +final AtomicInteger counter = new AtomicInteger(); +// tracks the downstream request amount +final AtomicLong requested = new AtomicLong(); + +// no more values expected from upstream +volatile boolean done; +// the upstream error if any +Throwable error; + +void drain() { + if (counter.getAndIncrement() != 0) { + return; + } + + int missed = 1; + Subscriber<? super T> child = this.child; + Queue<T> queue = this.queue; + + for (;;) { + long requests = requested.get(); + long emission = 0L; + + while (emission != requests) { // don't emit more than requested + if (child.isUnsubscribed()) { + return; + } + + boolean stop = done; // order matters here! + T t = queue.poll(); + boolean empty = t == null; + + // if no more values, emit an error or completion event + if (stop && empty) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + } else { + child.onCompleted(); + } + return; + } + // the upstream hasn't stopped yet but we don't have a value available + if (empty) { + break; + } + + child.onNext(t); + emission++; + } + + // if we are at a request boundary, a terminal event can be still emitted without requests + if (emission == requests) { + if (child.isUnsubscribed()) { + return; + } + + boolean stop = done; // order matters here! + boolean empty = queue.isEmpty(); + + // if no more values, emit an error or completion event + if (stop && empty) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + } else { + child.onCompleted(); + } + return; + } + } + + // decrement the current request amount by the emission count + if (emission != 0L && requests != Long.MAX_VALUE) { + BackpressureUtils.produced(requested, emission); + } + + // indicate that we have performed the outstanding amount of work + missed = counter.addAndGet(-missed); + if (missed == 0) { + return; + } + // if a concurrent getAndIncrement() happened, we loop back and continue + } +} +``` + +# Creating source operators + +One creates a source operator by implementing the `OnSubscribe` interface and then calls `Observable.create` with it: + +```java +OnSubscribe<T> onSubscribe = (Subscriber<? super T> child) -> { + // logic here +}; + +Observable<T> observable = Observable.create(onSubscribe); +``` + +*Note: a common mistake when writing an operator is that one simply calls `onNext` disregarding backpressure; one should use `fromCallable` instead for synchronously (blockingly) generating a single value.* + +The `logic here` could be arbitrary complex logic. Usually, one creates a class implementing `Subscription` and `Producer`, sets it on the `child` and works out the emission pattern: + +```java +OnSubscribe<T> onSubscribe = (Subscriber<? super T> child) -> { + MySubscription mys = new MySubscription(child, otherParams); + child.add(mys); + child.setProducer(mys); + + mys.runBusinessLogic(); +}; +``` + +## Converting a callback-API to reactive + +One of the reasons custom sources are created is when one converts a classical, callback-based 'reactive' API to RxJava. In this case, one has to setup the callback on the non-RxJava source and wire up unsubscription if possible: + +```java +OnSubscribe<Data> onSubscribe = (Subscriber<? super Data> child) -> { + Callback cb = event -> { + if (event.isSuccess()) { + child.setProducer(new SingleProducer<Data>(child, event.getData())); + } else { + child.onError(event.getError()); + } + }; + + Closeable c = api.query("someinput", cb); + + child.add(Subscriptions.create(() -> Closeables.closeQuietly(c))); +}; +``` + +In this example, the `api` takes a callback and returns a `Closeable`. Our handler signals the data by setting a `SingleProducer` of it to deal with downstream backpressure. If the downstream wants to cancel a running API call, the wrap to `Subscription` will close the query. + +However, in case the callback is called more than once, one has to deal with backpressure a different way. At this level, perhaps the most easiest way is to apply `onBackpressureBuffer` or `onBackpressureDrop` on the created `Observable`: + +```java +OnSubscribe<Data> onSubscribe = (Subscriber<? super Data> child) -> { + Callback cb = event -> { + if (event.isSuccess()) { + child.onNext(event.getData()); + } else { + child.onError(event.getError()); + } + }; + + Closeable c = api.query("someinput", cb); + + child.add(Subscriptions.create(() -> Closeables.closeQuietly(c))); +}; + +Observable<T> observable = Observable.create(onSubscribe).onBackpressureBuffer(); +``` + +# Creating intermediate operators + +Writing an intermediate operator is more difficult because one may need to coordinate request amount between the upstream and downstream. + +Intermediate operators are nothing but `Subscriber`s themselves, wrapping the downstream `Subscriber` themselves, modulating the calls to `onXXX`methods and they get subscribed to the upstream's `Observable`: + +```java +Func1<T, R> mapper = ... + +Observable<T> source = ... + +OnSubscribe<R> onSubscribe = (Subscriber<? super R> child) -> { + + source.subscribe(new MapSubscriber<T, R>(child) { + @Override + public void onNext(T t) { + child.onNext(function.call(t)); + } + + // ... etc + }); + +} +``` + +Depending on whether the safety-net of the `Observable.subscribe` method is too much of an overhead, one can call `Observable.unsafeSubscribe` but then the operator has to manage and unsubscribe its own resources manually. + +This approach has a common pattern that can be factored out - at the expense of more allocation and indirection - and became the `lift` operator. + +The `lift` operator takes an `Observable.Operator<R, T>` interface implementor where `R` is the output type towards the downstream and `T` is the input type from the upstream. In our example, we can rewrite the operator as follows: + +```java + +Operator<R, T> op = child -> + return new MapSubscriber<T, R>(child) { + @Override + public void onNext(T t) { + child.onNext(function.call(t)); + } + + // ... etc + }; +} + +source.lift(op)... +``` + +The constructor of `Subscriber(Subscriber<?>)` has some caveats: it shares the underlying resource management between `child` and `MapSubscriber`. This has the unfortunate effect that when the business logic calls `MapSubscriber.unsubscribe`, it may inadvertently unsubscribe the `child`'s resources prematurely. In addition, it sets up the `Subscriber` in a way that calls to `setProducer` are forwarded to the `child` as well. + +Sometimes it is acceptable, but generally one should avoid this coupling by implementing these custom `Subscriber`s among the following pattern: + +```java +public final class MapSubscriber<T, R> extends Subscriber<T> { + final Subscriber<? super R> child; + + final Function<T, R> mapper; + + public MapSubscriber(Subscriber<? super R> child, Func1<T, R> mapper) { + // no call to super(child) ! + this.child = child; + this.mapper = mapper; + + // prevent premature requesting + this.request(0); + } + + // setup the unsubscription and request links to downstream + void init() { + child.add(this); + child.setProducer(n -> requestMore(n)); + } + + @Override + public void onNext(T t) { + try { + child.onNext(mapper.call(t)); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + // if something crashed non-fatally, unsubscribe from upstream and signal the error + unsubscribe(); + onError(ex); + } + } + + @Override + public void onError(Throwable e) { + child.onError(e); + } + + @Override + public void onCompleted() { + child.onCompleted(); + } + + void requestMore(long n) { + // deal with the downstream requests + this.request(n); + } +} + +Operator<R, T> op = child -> { + MapSubscriber<T, R> parent = new MapSubscriber<T, R>(child, mapper); + parent.init(); + return parent; +} +``` + +Some operators may not emit the received value to the `child` subscriber (such as filter). In this case, one has to call `request(1)` to ask for a replenishment because the downstream doesn't know about the dropped value and won't request itself: + +```java +// ... + + @Override + public void onNext(T t) { + try { + if (predicate.call(t)) { + child.onNext(t); + } else { + request(1); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + unsubscribe(); + onError(ex); + } + } + +// ... +``` + +When an operator maps an `onNext` emission to a terminal event then before calling the terminal event it should unsubscribe the subscriber to upstream (usually called the parent). In addition, because upstream may (legally) do something like this: + +```java +child.onNext(blah); +// no check for unsubscribed here +child.onCompleted(); +``` + +we should ensure that the operator complies with the `Observable` contract and only emits one terminal event so we use a defensive done flag: + +```java +boolean done; // = false; + +@Override +public void onError(Throwable e) { + if (done) { + return; + } + done = true; + ... +} + +@Override +public void onCompleted(Throwable e) { + if (done) { + return; + } + done = true; + ... +} +``` + +An example of this pattern is seen in `OnSubscribeMap`. + +# Further reading + +Writing operators that consume multiple source `Observable`s or produce to multiple `Subscriber`s are the most difficult one to implement. + +For inspiration, see the [blog posts](http://akarnokd.blogspot.hu/) of @akarnokd about the RxJava internals. The reader is advised to read from the very first post on and keep reading in sequence. \ No newline at end of file diff --git a/docs/Mathematical-and-Aggregate-Operators.md b/docs/Mathematical-and-Aggregate-Operators.md new file mode 100644 index 0000000000..f6bf79019e --- /dev/null +++ b/docs/Mathematical-and-Aggregate-Operators.md @@ -0,0 +1,366 @@ +This page shows operators that perform mathematical or other operations over an entire sequence of items emitted by an `Observable` or `Flowable`. Because these operations must wait for the source `Observable`/`Flowable` to complete emitting items before they can construct their own emissions (and must usually buffer these items), these operators are dangerous to use on `Observable`s and `Flowable`s that may have very long or infinite sequences. + +# Outline + +- [Mathematical Operators](#mathematical-operators) + - [`averageDouble`](#averagedouble) + - [`averageFloat`](#averagefloat) + - [`max`](#max) + - [`min`](#min) + - [`sumDouble`](#sumdouble) + - [`sumFloat`](#sumfloat) + - [`sumInt`](#sumint) + - [`sumLong`](#sumlong) +- [Standard Aggregate Operators](#standard-aggregate-operators) + - [`count`](#count) + - [`reduce`](#reduce) + - [`reduceWith`](#reducewith) + - [`collect`](#collect) + - [`collectInto`](#collectinto) + - [`toList`](#tolist) + - [`toSortedList`](#tosortedlist) + - [`toMap`](#tomap) + - [`toMultimap`](#tomultimap) + +## Mathematical Operators + +> The operators in this section are part of the [`RxJava2Extensions`](https://github.com/akarnokd/RxJava2Extensions) project. You have to add the `rxjava2-extensions` module as a dependency to your project. It can be found at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.github.akarnokd%22). + +> Note that unlike the standard RxJava aggregator operators, these mathematical operators return `Observable` and `Flowable` instead of the `Single` or `Maybe`. + +*The examples below assume that the `MathObservable` and `MathFlowable` classes are imported from the `rxjava2-extensions` module:* + +```java +import hu.akarnokd.rxjava2.math.MathObservable; +import hu.akarnokd.rxjava2.math.MathFlowable; +``` + +### averageDouble + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/average.html](http://reactivex.io/documentation/operators/average.html) + +Calculates the average of `Number`s emitted by an `Observable` and emits this average as a `Double`. + +#### averageDouble example + +```java +Observable<Integer> numbers = Observable.just(1, 2, 3); +MathObservable.averageDouble(numbers).subscribe((Double avg) -> System.out.println(avg)); + +// prints 2.0 +``` + +### averageFloat + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/average.html](http://reactivex.io/documentation/operators/average.html) + +Calculates the average of `Number`s emitted by an `Observable` and emits this average as a `Float`. + +#### averageFloat example + +```java +Observable<Integer> numbers = Observable.just(1, 2, 3); +MathObservable.averageFloat(numbers).subscribe((Float avg) -> System.out.println(avg)); + +// prints 2.0 +``` + +### max + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/max.html](http://reactivex.io/documentation/operators/max.html) + +Emits the maximum value emitted by a source `Observable`. A `Comparator` can be specified that will be used to compare the elements emitted by the `Observable`. + +#### max example + +```java +Observable<Integer> numbers = Observable.just(4, 9, 5); +MathObservable.max(numbers).subscribe(System.out::println); + +// prints 9 +``` + +The following example specifies a `Comparator` to find the longest `String` in the source `Observable`: + +```java +final Observable<String> names = Observable.just("Kirk", "Spock", "Chekov", "Sulu"); +MathObservable.max(names, Comparator.comparingInt(String::length)) + .subscribe(System.out::println); + +// prints Chekov +``` + +### min + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/min.html](http://reactivex.io/documentation/operators/min.html) + +Emits the minimum value emitted by a source `Observable`. A `Comparator` can be specified that will be used to compare the elements emitted by the `Observable`. + +#### min example + +```java +Observable<Integer> numbers = Observable.just(4, 9, 5); +MathObservable.min(numbers).subscribe(System.out::println); + +// prints 4 +``` + +### sumDouble + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/sum.html](http://reactivex.io/documentation/operators/sum.html) + +Adds the `Double`s emitted by an `Observable` and emits this sum. + +#### sumDouble example + +```java +Observable<Double> numbers = Observable.just(1.0, 2.0, 3.0); +MathObservable.sumDouble(numbers).subscribe((Double sum) -> System.out.println(sum)); + +// prints 6.0 +``` + +### sumFloat + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/sum.html](http://reactivex.io/documentation/operators/sum.html) + +Adds the `Float`s emitted by an `Observable` and emits this sum. + +#### sumFloat example + +```java +Observable<Float> numbers = Observable.just(1.0F, 2.0F, 3.0F); +MathObservable.sumFloat(numbers).subscribe((Float sum) -> System.out.println(sum)); + +// prints 6.0 +``` + +### sumInt + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/sum.html](http://reactivex.io/documentation/operators/sum.html) + +Adds the `Integer`s emitted by an `Observable` and emits this sum. + +#### sumInt example + +```java +Observable<Integer> numbers = Observable.range(1, 100); +MathObservable.sumInt(numbers).subscribe((Integer sum) -> System.out.println(sum)); + +// prints 5050 +``` + +### sumLong + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/sum.html](http://reactivex.io/documentation/operators/sum.html) + +Adds the `Long`s emitted by an `Observable` and emits this sum. + +#### sumLong example + +```java +Observable<Long> numbers = Observable.rangeLong(1L, 100L); +MathObservable.sumLong(numbers).subscribe((Long sum) -> System.out.println(sum)); + +// prints 5050 +``` + +## Standard Aggregate Operators + +> Note that these standard aggregate operators return a `Single` or `Maybe` because the number of output items is always know to be at most one. + +### count + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/count.html](http://reactivex.io/documentation/operators/count.html) + +Counts the number of items emitted by an `Observable` and emits this count as a `Long`. + +#### count example + +```java +Observable.just(1, 2, 3).count().subscribe(System.out::println); + +// prints 3 +``` + +### reduce + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/reduce.html](http://reactivex.io/documentation/operators/reduce.html) + +Apply a function to each emitted item, sequentially, and emit only the final accumulated value. + +#### reduce example + +```java +Observable.range(1, 5) + .reduce((product, x) -> product * x) + .subscribe(System.out::println); + +// prints 120 +``` + +### reduceWith + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/reduce.html](http://reactivex.io/documentation/operators/reduce.html) + +Apply a function to each emitted item, sequentially, and emit only the final accumulated value. + + +#### reduceWith example + +```java +Observable.just(1, 2, 2, 3, 4, 4, 4, 5) + .reduceWith(TreeSet::new, (set, x) -> { + set.add(x); + return set; + }) + .subscribe(System.out::println); + +// prints [1, 2, 3, 4, 5] +``` + +### collect + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/reduce.html](http://reactivex.io/documentation/operators/reduce.html) + +Collect items emitted by the source `Observable` into a single mutable data structure and return an `Observable` that emits this structure. + +#### collect example + +```java +Observable.just("Kirk", "Spock", "Chekov", "Sulu") + .collect(() -> new StringJoiner(" \uD83D\uDD96 "), StringJoiner::add) + .map(StringJoiner::toString) + .subscribe(System.out::println); + +// prints Kirk 🖖 Spock 🖖 Chekov 🖖 Sulu +``` + +### collectInto + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/reduce.html](http://reactivex.io/documentation/operators/reduce.html) + +Collect items emitted by the source `Observable` into a single mutable data structure and return an `Observable` that emits this structure. + +#### collectInto example + +*Note: the mutable value that will collect the items (here the `StringBuilder`) will be shared between multiple subscribers.* + +```java +Observable.just('R', 'x', 'J', 'a', 'v', 'a') + .collectInto(new StringBuilder(), StringBuilder::append) + .map(StringBuilder::toString) + .subscribe(System.out::println); + +// prints RxJava +``` + +### toList + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/to.html](http://reactivex.io/documentation/operators/to.html) + +Collect all items from an `Observable` and emit them as a single `List`. + +#### toList example + +```java +Observable.just(2, 1, 3) + .toList() + .subscribe(System.out::println); + +// prints [2, 1, 3] +``` + +### toSortedList + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/to.html](http://reactivex.io/documentation/operators/to.html) + +Collect all items from an `Observable` and emit them as a single, sorted `List`. + +#### toSortedList example + +```java +Observable.just(2, 1, 3) + .toSortedList(Comparator.reverseOrder()) + .subscribe(System.out::println); + +// prints [3, 2, 1] +``` + +### toMap + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/to.html](http://reactivex.io/documentation/operators/to.html) + +Convert the sequence of items emitted by an `Observable` into a `Map` keyed by a specified key function. + +#### toMap example + +```java +Observable.just(1, 2, 3, 4) + .toMap((x) -> { + // defines the key in the Map + return x; + }, (x) -> { + // defines the value that is mapped to the key + return (x % 2 == 0) ? "even" : "odd"; + }) + .subscribe(System.out::println); + +// prints {1=odd, 2=even, 3=odd, 4=even} +``` + +### toMultimap + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/to.html](http://reactivex.io/documentation/operators/to.html) + +Convert the sequence of items emitted by an `Observable` into a `Collection` that is also a `Map` keyed by a specified key function. + +#### toMultimap example + +```java +Observable.just(1, 2, 3, 4) + .toMultimap((x) -> { + // defines the key in the Map + return (x % 2 == 0) ? "even" : "odd"; + }, (x) -> { + // defines the value that is mapped to the key + return x; + }) + .subscribe(System.out::println); + +// prints {even=[2, 4], odd=[1, 3]} +``` diff --git a/docs/Observable-Utility-Operators.md b/docs/Observable-Utility-Operators.md new file mode 100644 index 0000000000..b23c871894 --- /dev/null +++ b/docs/Observable-Utility-Operators.md @@ -0,0 +1,28 @@ +This page lists various utility operators for working with Observables. + +* [**`materialize( )`**](http://reactivex.io/documentation/operators/materialize-dematerialize.html) — convert an Observable into a list of Notifications +* [**`dematerialize( )`**](http://reactivex.io/documentation/operators/materialize-dematerialize.html) — convert a materialized Observable back into its non-materialized form +* [**`timestamp( )`**](http://reactivex.io/documentation/operators/timestamp.html) — attach a timestamp to every item emitted by an Observable +* [**`serialize( )`**](http://reactivex.io/documentation/operators/serialize.html) — force an Observable to make serialized calls and to be well-behaved +* [**`cache( )`**](http://reactivex.io/documentation/operators/replay.html) — remember the sequence of items emitted by the Observable and emit the same sequence to future Subscribers +* [**`observeOn( )`**](http://reactivex.io/documentation/operators/observeon.html) — specify on which Scheduler a Subscriber should observe the Observable +* [**`subscribeOn( )`**](http://reactivex.io/documentation/operators/subscribeon.html) — specify which Scheduler an Observable should use when its subscription is invoked +* [**`doOnEach( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to take whenever an Observable emits an item +* [**`doOnNext( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to call just before the Observable passes an `onNext` event along to its downstream +* [**`doAfterNext( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to call after the Observable has passed an `onNext` event along to its downstream +* [**`doOnCompleted( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to take when an Observable completes successfully +* [**`doOnError( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to take when an Observable completes with an error +* [**`doOnTerminate( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to call just before an Observable terminates, either successfully or with an error +* [**`doAfterTerminate( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to call just after an Observable terminated, either successfully or with an error +* [**`doOnSubscribe( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to take when an observer subscribes to an Observable +* *1.x* [**`doOnUnsubscribe( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to take when an observer unsubscribes from an Observable +* [**`finallyDo( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to take when an Observable completes +* [**`doFinally( )`**](http://reactivex.io/documentation/operators/do.html) — register an action to call when an Observable terminates or it gets disposed +* [**`delay( )`**](http://reactivex.io/documentation/operators/delay.html) — shift the emissions from an Observable forward in time by a specified amount +* [**`delaySubscription( )`**](http://reactivex.io/documentation/operators/delay.html) — hold an Subscriber's subscription request for a specified amount of time before passing it on to the source Observable +* [**`timeInterval( )`**](http://reactivex.io/documentation/operators/timeinterval.html) — emit the time lapsed between consecutive emissions of a source Observable +* [**`using( )`**](http://reactivex.io/documentation/operators/using.html) — create a disposable resource that has the same lifespan as an Observable +* [**`single( )`**](http://reactivex.io/documentation/operators/first.html) — if the Observable completes after emitting a single item, return that item, otherwise throw an exception +* [**`singleOrDefault( )`**](http://reactivex.io/documentation/operators/first.html) — if the Observable completes after emitting a single item, return that item, otherwise return a default item +* [**`repeat( )`**](http://reactivex.io/documentation/operators/repeat.html) — create an Observable that emits a particular item or sequence of items repeatedly +* [**`repeatWhen( )`**](http://reactivex.io/documentation/operators/repeat.html) — create an Observable that emits a particular item or sequence of items repeatedly, depending on the emissions of a second Observable \ No newline at end of file diff --git a/docs/Observable.md b/docs/Observable.md new file mode 100644 index 0000000000..fec5467908 --- /dev/null +++ b/docs/Observable.md @@ -0,0 +1,3 @@ +In RxJava an object that implements the _Observer_ interface _subscribes_ to an object of the _Observable_ class. Then that subscriber reacts to whatever item or sequence of items the Observable object _emits_. This pattern facilitates concurrent operations because it does not need to block while waiting for the Observable to emit objects, but instead it creates a sentry in the form of a subscriber that stands ready to react appropriately at whatever future time the Observable does so. + +For information about the Observable class, see [the Observable documentation page at ReactiveX.io](http://reactivex.io/documentation/observable.html). \ No newline at end of file diff --git a/docs/Operator-Matrix.md b/docs/Operator-Matrix.md new file mode 100644 index 0000000000..afeb8e4182 --- /dev/null +++ b/docs/Operator-Matrix.md @@ -0,0 +1,359 @@ +Operator | ![Flowable](https://raw.github.com/wiki/ReactiveX/RxJava/images/opmatrix-flowable.png) | ![Observable](https://raw.github.com/wiki/ReactiveX/RxJava/images/opmatrix-observable.png) | ![Maybe](https://raw.github.com/wiki/ReactiveX/RxJava/images/opmatrix-maybe.png) | ![Single](https://raw.github.com/wiki/ReactiveX/RxJava/images/opmatrix-single.png) | ![Completable](https://raw.github.com/wiki/ReactiveX/RxJava/images/opmatrix-completable.png) | +-----|---|---|---|---|---| +<a name='all'></a>`all`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use contains().'>([1](#notes-1))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use contains().'>([1](#notes-1))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='amb'></a>`amb`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='ambArray'></a>`ambArray`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='ambWith'></a>`ambWith`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='andThen'></a>`andThen`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use concatWith.'>([3](#notes-3))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use concatWith.'>([3](#notes-3))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use concatWith.'>([3](#notes-3))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use concatWith.'>([3](#notes-3))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='any'></a>`any`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use contains().'>([1](#notes-1))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use contains().'>([1](#notes-1))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='blockingAwait'></a>`blockingAwait`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use blockingFirst().'>([4](#notes-4))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use blockingFirst().'>([4](#notes-4))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use blockingGet().'>([5](#notes-5))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use blockingGet().'>([5](#notes-5))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='blockingFirst'></a>`blockingFirst`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No elements to get. Use blockingAwait().'>([7](#notes-7))</sup>| +<a name='blockingForEach'></a>`blockingForEach`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use blockingSubscribe()'>([8](#notes-8))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use blockingSubscribe()'>([8](#notes-8))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use blockingSubscribe()'>([8](#notes-8))</sup>| +<a name='blockingGet'></a>`blockingGet`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use blockingFirst().'>([4](#notes-4))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use blockingFirst().'>([4](#notes-4))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No elements to get. Use blockingAwait().'>([7](#notes-7))</sup>| +<a name='blockingIterable'></a>`blockingIterable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No elements to get. Use blockingAwait().'>([7](#notes-7))</sup>| +<a name='blockingLast'></a>`blockingLast`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No elements to get. Use blockingAwait().'>([7](#notes-7))</sup>| +<a name='blockingLatest'></a>`blockingLatest`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No elements to get. Use blockingAwait().'>([7](#notes-7))</sup>| +<a name='blockingMostRecent'></a>`blockingMostRecent`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No elements to get. Use blockingAwait().'>([7](#notes-7))</sup>| +<a name='blockingNext'></a>`blockingNext`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No elements to get. Use blockingAwait().'>([7](#notes-7))</sup>| +<a name='blockingSingle'></a>`blockingSingle`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No elements to get. Use blockingAwait().'>([7](#notes-7))</sup>| +<a name='blockingStream'></a>`blockingStream`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to get. Use blockingGet().'>([6](#notes-6))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No elements to get. Use blockingAwait().'>([7](#notes-7))</sup>| +<a name='blockingSubscribe'></a>`blockingSubscribe`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='buffer'></a>`buffer`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use map() to transform into a list/collection.'>([9](#notes-9))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use map() to transform into a list/collection.'>([10](#notes-10))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to bring in a list/collection.'>([11](#notes-11))</sup>| +<a name='cache'></a>`cache`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='cacheWithInitialCapacity'></a>`cacheWithInitialCapacity`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to store. Use cache().'>([12](#notes-12))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to store. Use cache().'>([12](#notes-12))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to store. Use cache().'>([12](#notes-12))</sup>| +<a name='cast'></a>`cast`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='collect'></a>`collect`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to collect. Use map() to transform into a list/collection.'>([13](#notes-13))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='One element to collect. Use map() to transform into a list/collection.'>([14](#notes-14))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to bring in a collection.'>([15](#notes-15))</sup>| +<a name='collectInto'></a>`collectInto`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to collect. Use map() to transform into a list/collection.'>([13](#notes-13))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='One element to collect. Use map() to transform into a list/collection.'>([14](#notes-14))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to bring in a collection.'>([15](#notes-15))</sup>| +<a name='combineLatest'></a>`combineLatest`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element per source. Use zip().'>([16](#notes-16))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element per source. Use zip().'>([16](#notes-16))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use merge().'>([17](#notes-17))</sup>| +<a name='combineLatestArray'></a>`combineLatestArray`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element per source. Use zipArray().'>([18](#notes-18))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element per source. Use zipArray().'>([18](#notes-18))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use mergeArray().'>([19](#notes-19))</sup>| +<a name='combineLatestArrayDelayError'></a>`combineLatestArrayDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element per source. Use zipArray().'>([18](#notes-18))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element per source. Use zipArray().'>([18](#notes-18))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use mergeArrayDelayError().'>([20](#notes-20))</sup>| +<a name='combineLatestDelayError'></a>`combineLatestDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element per source. Use zip().'>([16](#notes-16))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element per source. Use zip().'>([16](#notes-16))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use mergeDelayError().'>([21](#notes-21))</sup>| +<a name='complete'></a>`complete`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use empty().'>([22](#notes-22))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use empty().'>([22](#notes-22))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use empty().'>([22](#notes-22))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Never empty.'>([23](#notes-23))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='compose'></a>`compose`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='concat'></a>`concat`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='concatArray'></a>`concatArray`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='concatArrayDelayError'></a>`concatArrayDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='concatArrayEager'></a>`concatArrayEager`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No items to keep ordered. Use mergeArray().'>([24](#notes-24))</sup>| +<a name='concatArrayEagerDelayError'></a>`concatArrayEagerDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No items to keep ordered. Use mergeArrayDelayError().'>([25](#notes-25))</sup>| +<a name='concatDelayError'></a>`concatDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='concatEager'></a>`concatEager`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No items to keep ordered. Use merge().'>([26](#notes-26))</sup>| +<a name='concatEagerDelayError'></a>`concatEagerDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='No items to keep ordered. Use mergeDelayError().'>([27](#notes-27))</sup>| +<a name='concatMap'></a>`concatMap`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapCompletable'></a>`concatMapCompletable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapCompletableDelayError'></a>`concatMapCompletableDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use concatMapCompletable.'>([29](#notes-29))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use concatMapCompletable.'>([29](#notes-29))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapDelayError'></a>`concatMapDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use concatMap.'>([30](#notes-30))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use concatMap.'>([30](#notes-30))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapEager'></a>`concatMapEager`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item to map. Use concatMap().'>([31](#notes-31))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item to map. Use concatMap().'>([31](#notes-31))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapEagerDelayError'></a>`concatMapEagerDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item to map. Use concatMap().'>([31](#notes-31))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item to map. Use concatMap().'>([31](#notes-31))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapIterable'></a>`concatMapIterable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flattenAsFlowable.'>([32](#notes-32))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flattenAsFlowable.'>([32](#notes-32))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapMaybe'></a>`concatMapMaybe`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use concatMap.'>([33](#notes-33))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapMaybeDelayError'></a>`concatMapMaybeDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use concatMapMaybe.'>([34](#notes-34))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use concatMapMaybe.'>([34](#notes-34))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapSingle'></a>`concatMapSingle`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use concatMap().'>([35](#notes-35))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapSingleDelayError'></a>`concatMapSingleDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use concatMapSingle.'>([36](#notes-36))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use concatMapSingle.'>([36](#notes-36))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatMapStream'></a>`concatMapStream`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flattenStreamAsFlowable.'>([37](#notes-37))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flattenStreamAsFlowable.'>([37](#notes-37))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='concatWith'></a>`concatWith`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='contains'></a>`contains`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='count'></a>`count`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Never empty thus always 1.'>([38](#notes-38))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus always 0.'>([39](#notes-39))</sup>| +<a name='create'></a>`create`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='debounce'></a>`debounce`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item signaled so no subsequent items to work with.'>([40](#notes-40))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item signaled so no subsequent items to work with.'>([40](#notes-40))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='defaultIfEmpty'></a>`defaultIfEmpty`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Never empty.'>([23](#notes-23))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to chose the follow-up sequence.'>([42](#notes-42))</sup>| +<a name='defer'></a>`defer`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='delay'></a>`delay`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='delaySubscription'></a>`delaySubscription`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='dematerialize'></a>`dematerialize`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='distinct'></a>`distinct`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, always distinct.'>([43](#notes-43))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, always distinct.'>([43](#notes-43))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='distinctUntilChanged'></a>`distinctUntilChanged`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, always distinct.'>([43](#notes-43))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, always distinct.'>([43](#notes-43))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='doAfterNext'></a>`doAfterNext`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doAfterSuccess().'>([44](#notes-44))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doAfterSuccess().'>([44](#notes-44))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='doAfterSuccess'></a>`doAfterSuccess`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doAfterNext().'>([45](#notes-45))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doAfterNext().'>([45](#notes-45))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='doAfterTerminate'></a>`doAfterTerminate`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='doFinally'></a>`doFinally`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='doOnCancel'></a>`doOnCancel`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doOnDispose().'>([46](#notes-46))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doOnDispose().'>([46](#notes-46))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doOnDispose().'>([46](#notes-46))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doOnDispose().'>([46](#notes-46))</sup>| +<a name='doOnComplete'></a>`doOnComplete`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always succeeds or fails, there is no onComplete signal.'>([47](#notes-47))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='doOnDispose'></a>`doOnDispose`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doOnCancel().'>([48](#notes-48))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='doOnEach'></a>`doOnEach`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use doOnEvent().'>([49](#notes-49))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use doOnEvent().'>([49](#notes-49))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='doOnError'></a>`doOnError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='doOnEvent'></a>`doOnEvent`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use doOnEach().'>([50](#notes-50))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use doOnEach().'>([50](#notes-50))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='doOnLifecycle'></a>`doOnLifecycle`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='doOnNext'></a>`doOnNext`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doOnSuccess().'>([51](#notes-51))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doOnSuccess().'>([51](#notes-51))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='doOnRequest'></a>`doOnRequest`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>| +<a name='doOnSubscribe'></a>`doOnSubscribe`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='doOnSuccess'></a>`doOnSuccess`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doOnNext().'>([53](#notes-53))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Different terminology. Use doOnNext().'>([53](#notes-53))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='doOnTerminate'></a>`doOnTerminate`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='elementAt'></a>`elementAt`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item with index 0. Use defaultIfEmpty.'>([54](#notes-54))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item with index 0.'>([55](#notes-55))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='elementAtOrError'></a>`elementAtOrError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item with index 0. Use toSingle.'>([56](#notes-56))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item with index 0.'>([55](#notes-55))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='empty'></a>`empty`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Never empty.'>([23](#notes-23))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use complete().'>([57](#notes-57))</sup>| +<a name='error'></a>`error`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='filter'></a>`filter`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='first'></a>`first`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use defaultIfEmpty.'>([58](#notes-58))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item.'>([59](#notes-59))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to chose the follow-up sequence.'>([42](#notes-42))</sup>| +<a name='firstElement'></a>`firstElement`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item, would be no-op.'>([61](#notes-61))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='firstOrError'></a>`firstOrError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item, would be no-op.'>([61](#notes-61))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen().'>([62](#notes-62))</sup>| +<a name='firstOrErrorStage'></a>`firstOrErrorStage`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen().'>([64](#notes-64))</sup>| +<a name='firstStage'></a>`firstStage`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>| +<a name='flatMap'></a>`flatMap`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flatMapCompletable'></a>`flatMapCompletable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flatMapIterable'></a>`flatMapIterable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flattenAsFlowable.'>([32](#notes-32))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flattenAsFlowable.'>([32](#notes-32))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flatMapMaybe'></a>`flatMapMaybe`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMap().'>([65](#notes-65))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flatMapObservable'></a>`flatMapObservable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Not supported. Use flatMap.'>([66](#notes-66))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMap.'>([67](#notes-67))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flatMapPublisher'></a>`flatMapPublisher`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMap.'>([67](#notes-67))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Not supported. Use flatMap.'>([68](#notes-68))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flatMapSingle'></a>`flatMapSingle`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMap().'>([65](#notes-65))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flatMapStream'></a>`flatMapStream`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flattenStreamAsFlowable.'>([37](#notes-37))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flattenStreamAsFlowable.'>([37](#notes-37))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flattenAsFlowable'></a>`flattenAsFlowable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMapIterable().'>([69](#notes-69))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMapIterable().'>([69](#notes-69))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flattenAsObservable'></a>`flattenAsObservable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMapIterable().'>([69](#notes-69))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMapIterable().'>([69](#notes-69))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flattenStreamAsFlowable'></a>`flattenStreamAsFlowable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMapStream().'>([70](#notes-70))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMapStream().'>([70](#notes-70))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='flattenStreamAsObservable'></a>`flattenStreamAsObservable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMapStream().'>([70](#notes-70))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use flatMapStream().'>([70](#notes-70))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='forEach'></a>`forEach`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use subscribe().'>([71](#notes-71))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use subscribe().'>([71](#notes-71))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use subscribe().'>([71](#notes-71))</sup>| +<a name='forEachWhile'></a>`forEachWhile`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use subscribe().'>([71](#notes-71))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use subscribe().'>([71](#notes-71))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use subscribe().'>([71](#notes-71))</sup>| +<a name='fromAction'></a>`fromAction`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Never empty.'>([23](#notes-23))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='fromArray'></a>`fromArray`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use just().'>([72](#notes-72))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item. Use just().'>([73](#notes-73))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use complete().'>([74](#notes-74))</sup>| +<a name='fromCallable'></a>`fromCallable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='fromCompletable'></a>`fromCompletable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always error.'>([75](#notes-75))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use wrap().'>([76](#notes-76))</sup>| +<a name='fromCompletionStage'></a>`fromCompletionStage`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='fromFuture'></a>`fromFuture`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='fromIterable'></a>`fromIterable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use just().'>([72](#notes-72))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item. Use just().'>([73](#notes-73))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use complete().'>([74](#notes-74))</sup>| +<a name='fromMaybe'></a>`fromMaybe`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use wrap().'>([76](#notes-76))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='fromObservable'></a>`fromObservable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use wrap().'>([76](#notes-76))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='fromOptional'></a>`fromOptional`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item. Use just().'>([73](#notes-73))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use complete().'>([74](#notes-74))</sup>| +<a name='fromPublisher'></a>`fromPublisher`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='fromRunnable'></a>`fromRunnable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Never empty.'>([23](#notes-23))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='fromSingle'></a>`fromSingle`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use wrap().'>([76](#notes-76))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='fromStream'></a>`fromStream`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use just().'>([72](#notes-72))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item. Use just().'>([73](#notes-73))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use complete().'>([74](#notes-74))</sup>| +<a name='fromSupplier'></a>`fromSupplier`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='generate'></a>`generate`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use fromSupplier().'>([77](#notes-77))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use fromSupplier().'>([77](#notes-77))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use fromSupplier().'>([77](#notes-77))</sup>| +<a name='groupBy'></a>`groupBy`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item.'>([78](#notes-78))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item.'>([78](#notes-78))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to group.'>([79](#notes-79))</sup>| +<a name='groupJoin'></a>`groupJoin`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item.'>([78](#notes-78))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item.'>([78](#notes-78))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to join.'>([80](#notes-80))</sup>| +<a name='hide'></a>`hide`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='ignoreElement'></a>`ignoreElement`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use ignoreElements().'>([81](#notes-81))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use ignoreElements().'>([81](#notes-81))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='ignoreElements'></a>`ignoreElements`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use ignoreElement().'>([82](#notes-82))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use ignoreElement().'>([82](#notes-82))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='interval'></a>`interval`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use timer().'>([83](#notes-83))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use timer().'>([83](#notes-83))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use timer().'>([83](#notes-83))</sup>| +<a name='intervalRange'></a>`intervalRange`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use timer().'>([83](#notes-83))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use timer().'>([83](#notes-83))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use timer().'>([83](#notes-83))</sup>| +<a name='isEmpty'></a>`isEmpty`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item.'>([59](#notes-59))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='join'></a>`join`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use zip()'>([84](#notes-84))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use zip()'>([84](#notes-84))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to join.'>([80](#notes-80))</sup>| +<a name='just'></a>`just`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='last'></a>`last`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use defaultIfEmpty.'>([58](#notes-58))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item.'>([59](#notes-59))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to chose the follow-up sequence.'>([42](#notes-42))</sup>| +<a name='lastElement'></a>`lastElement`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item, would be no-op.'>([61](#notes-61))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='lastOrError'></a>`lastOrError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item, would be no-op.'>([61](#notes-61))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen().'>([62](#notes-62))</sup>| +<a name='lastOrErrorStage'></a>`lastOrErrorStage`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen().'>([64](#notes-64))</sup>| +<a name='lastStage'></a>`lastStage`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>| +<a name='lift'></a>`lift`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='map'></a>`map`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='mapOptional'></a>`mapOptional`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='materialize'></a>`materialize`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='merge'></a>`merge`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='mergeArray'></a>`mergeArray`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='mergeArrayDelayError'></a>`mergeArrayDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='mergeDelayError'></a>`mergeDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='mergeWith'></a>`mergeWith`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='never'></a>`never`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='observeOn'></a>`observeOn`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='ofType'></a>`ofType`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to filter.'>([85](#notes-85))</sup>| +<a name='onBackpressureBuffer'></a>`onBackpressureBuffer`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>| +<a name='onBackpressureDrop'></a>`onBackpressureDrop`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>| +<a name='onBackpressureLatest'></a>`onBackpressureLatest`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>| +<a name='onErrorComplete'></a>`onErrorComplete`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='onErrorResumeNext'></a>`onErrorResumeNext`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='onErrorResumeWith'></a>`onErrorResumeWith`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='onErrorReturn'></a>`onErrorReturn`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='onErrorReturnItem'></a>`onErrorReturnItem`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='onTerminateDetach'></a>`onTerminateDetach`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='parallel'></a>`parallel`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Needs backpressure thus not supported outside Flowable.'>([86](#notes-86))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Needs backpressure thus not supported outside Flowable.'>([86](#notes-86))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Needs backpressure thus not supported outside Flowable.'>([86](#notes-86))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Needs backpressure thus not supported outside Flowable.'>([86](#notes-86))</sup>| +<a name='publish'></a>`publish`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Connectable sources not supported outside Flowable and Observable. Use a MaybeSubject.'>([87](#notes-87))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Connectable sources not supported outside Flowable and Observable. Use a SingleSubject.'>([88](#notes-88))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Connectable sources not supported outside Flowable and Observable. Use a ConnectableSubject.'>([89](#notes-89))</sup>| +<a name='range'></a>`range`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use just().'>([90](#notes-90))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use just().'>([90](#notes-90))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use complete().'>([74](#notes-74))</sup>| +<a name='rangeLong'></a>`rangeLong`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use just().'>([90](#notes-90))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use just().'>([90](#notes-90))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use complete().'>([74](#notes-74))</sup>| +<a name='rebatchRequests'></a>`rebatchRequests`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Backpressure related and not supported outside Flowable.'>([52](#notes-52))</sup>| +<a name='reduce'></a>`reduce`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use map().'>([91](#notes-91))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use map().'>([91](#notes-91))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to reduce.'>([92](#notes-92))</sup>| +<a name='reduceWith'></a>`reduceWith`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use map().'>([91](#notes-91))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use map().'>([91](#notes-91))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to reduce.'>([92](#notes-92))</sup>| +<a name='repeat'></a>`repeat`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='repeatUntil'></a>`repeatUntil`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='repeatWhen'></a>`repeatWhen`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='replay'></a>`replay`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Connectable sources not supported outside Flowable and Observable. Use a MaybeSubject.'>([87](#notes-87))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Connectable sources not supported outside Flowable and Observable. Use a SingleSubject.'>([88](#notes-88))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Connectable sources not supported outside Flowable and Observable. Use a ConnectableSubject.'>([89](#notes-89))</sup>| +<a name='retry'></a>`retry`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='retryUntil'></a>`retryUntil`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='retryWhen'></a>`retryWhen`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='safeSubscribe'></a>`safeSubscribe`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='sample'></a>`sample`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='scan'></a>`scan`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use map().'>([91](#notes-91))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use map().'>([91](#notes-91))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to reduce.'>([92](#notes-92))</sup>| +<a name='scanWith'></a>`scanWith`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use map().'>([91](#notes-91))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use map().'>([91](#notes-91))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to reduce.'>([92](#notes-92))</sup>| +<a name='sequenceEqual'></a>`sequenceEqual`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='serialize'></a>`serialize`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one signal type.'>([93](#notes-93))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one signal type.'>([93](#notes-93))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one signal type.'>([93](#notes-93))</sup>| +<a name='share'></a>`share`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Connectable sources not supported outside Flowable and Observable. Use a MaybeSubject.'>([87](#notes-87))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Connectable sources not supported outside Flowable and Observable. Use a SingleSubject.'>([88](#notes-88))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Connectable sources not supported outside Flowable and Observable. Use a ConnectableSubject.'>([89](#notes-89))</sup>| +<a name='single'></a>`single`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use defaultIfEmpty.'>([58](#notes-58))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item.'>([59](#notes-59))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to chose the follow-up sequence.'>([42](#notes-42))</sup>| +<a name='singleElement'></a>`singleElement`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item, would be no-op.'>([61](#notes-61))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='singleOrError'></a>`singleOrError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always one item, would be no-op.'>([61](#notes-61))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen().'>([62](#notes-62))</sup>| +<a name='singleOrErrorStage'></a>`singleOrErrorStage`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen().'>([64](#notes-64))</sup>| +<a name='singleStage'></a>`singleStage`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use toCompletionStage().'>([63](#notes-63))</sup>| +<a name='skip'></a>`skip`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>| +<a name='skipLast'></a>`skipLast`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>| +<a name='skipUntil'></a>`skipUntil`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use takeUntil().'>([94](#notes-94))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use takeUntil().'>([94](#notes-94))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use takeUntil().'>([94](#notes-94))</sup>| +<a name='skipWhile'></a>`skipWhile`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use filter().'>([95](#notes-95))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use filter().'>([95](#notes-95))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='sorted'></a>`sorted`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item.'>([78](#notes-78))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item.'>([78](#notes-78))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item.'>([78](#notes-78))</sup>| +<a name='startWith'></a>`startWith`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='startWithArray'></a>`startWithArray`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use startWith() of Flowable or Observable.'>([96](#notes-96))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use startWith() of Flowable or Observable.'>([96](#notes-96))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use startWith() of Flowable or Observable.'>([96](#notes-96))</sup>| +<a name='startWithItem'></a>`startWithItem`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use startWith() of another reactive type.'>([97](#notes-97))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use startWith() of another reactive type.'>([97](#notes-97))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use startWith() of another reactive type.'>([97](#notes-97))</sup>| +<a name='startWithIterable'></a>`startWithIterable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use startWith() of Flowable or Observable.'>([98](#notes-98))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use startWith() of Flowable or Observable.'>([98](#notes-98))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use startWith() of Flowable or Observable.'>([98](#notes-98))</sup>| +<a name='subscribe'></a>`subscribe`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='subscribeOn'></a>`subscribeOn`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='subscribeWith'></a>`subscribeWith`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='switchIfEmpty'></a>`switchIfEmpty`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Never empty.'>([23](#notes-23))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use defaultIfEmpty().'>([99](#notes-99))</sup>| +<a name='switchMap'></a>`switchMap`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='switchMapCompletable'></a>`switchMapCompletable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='switchMapCompletableDelayError'></a>`switchMapCompletableDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='switchMapDelayError'></a>`switchMapDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='switchMapMaybe'></a>`switchMapMaybe`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='switchMapMaybeDelayError'></a>`switchMapMaybeDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='switchMapSingle'></a>`switchMapSingle`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='switchMapSingleDelayError'></a>`switchMapSingleDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use flatMap().'>([100](#notes-100))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to map.'>([28](#notes-28))</sup>| +<a name='switchOnNext'></a>`switchOnNext`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='switchOnNextDelayError'></a>`switchOnNextDelayError`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='take'></a>`take`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>| +<a name='takeLast'></a>`takeLast`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item, would be no-op.'>([60](#notes-60))</sup>| +<a name='takeUntil'></a>`takeUntil`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='takeWhile'></a>`takeWhile`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use filter().'>([95](#notes-95))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item. Use filter().'>([95](#notes-95))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty.'>([2](#notes-2))</sup>| +<a name='test'></a>`test`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='throttleFirst'></a>`throttleFirst`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item signaled so no subsequent items to work with.'>([40](#notes-40))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item signaled so no subsequent items to work with.'>([40](#notes-40))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='throttleLast'></a>`throttleLast`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item signaled so no subsequent items to work with.'>([40](#notes-40))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item signaled so no subsequent items to work with.'>([40](#notes-40))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='throttleLatest'></a>`throttleLatest`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item signaled so no subsequent items to work with.'>([40](#notes-40))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item signaled so no subsequent items to work with.'>([40](#notes-40))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='throttleWithTimeout'></a>`throttleWithTimeout`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item signaled so no subsequent items to work with.'>([40](#notes-40))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one item signaled so no subsequent items to work with.'>([40](#notes-40))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='timeInterval'></a>`timeInterval`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='timeout'></a>`timeout`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='timer'></a>`timer`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='timestamp'></a>`timestamp`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty thus no items to work with.'>([41](#notes-41))</sup>| +<a name='to'></a>`to`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='toCompletionStage'></a>`toCompletionStage`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use firstStage.'>([101](#notes-101))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use firstStage.'>([101](#notes-101))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='toFlowable'></a>`toFlowable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Would be no-op.'>([102](#notes-102))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='toFuture'></a>`toFuture`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='toList'></a>`toList`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to collect. Use map() to transform into a list/collection.'>([13](#notes-13))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='One element to collect. Use map() to transform into a list/collection.'>([14](#notes-14))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to bring in a collection.'>([15](#notes-15))</sup>| +<a name='toMap'></a>`toMap`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to collect. Use map() to transform into a list/collection.'>([13](#notes-13))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='One element to collect. Use map() to transform into a list/collection.'>([14](#notes-14))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to bring in a collection.'>([15](#notes-15))</sup>| +<a name='toMaybe'></a>`toMaybe`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use firstElement.'>([103](#notes-103))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use firstElement.'>([103](#notes-103))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Would be no-op.'>([102](#notes-102))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='toMultimap'></a>`toMultimap`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to collect. Use map() to transform into a list/collection.'>([13](#notes-13))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='One element to collect. Use map() to transform into a list/collection.'>([14](#notes-14))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to bring in a collection.'>([15](#notes-15))</sup>| +<a name='toObservable'></a>`toObservable`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Would be no-op.'>([102](#notes-102))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='toSingle'></a>`toSingle`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use firstOrError.'>([104](#notes-104))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use firstOrError.'>([104](#notes-104))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Would be no-op.'>([102](#notes-102))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='toSingleDefault'></a>`toSingleDefault`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use first.'>([105](#notes-105))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use first.'>([105](#notes-105))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use defaultIfEmpty().'>([106](#notes-106))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Would be no-op.'>([102](#notes-102))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='toSortedList'></a>`toSortedList`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element to collect. Use map() to transform into a list/collection.'>([13](#notes-13))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='One element to collect. Use map() to transform into a list/collection.'>([14](#notes-14))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to bring in a collection.'>([15](#notes-15))</sup>| +<a name='unsafeCreate'></a>`unsafeCreate`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='unsubscribeOn'></a>`unsubscribeOn`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='using'></a>`using`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='window'></a>`window`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use map() to transform into a nested source.'>([107](#notes-107))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use map() to transform into a nested source.'>([108](#notes-108))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use andThen() to bring in a nested source.'>([109](#notes-109))</sup>| +<a name='withLatestFrom'></a>`withLatestFrom`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element per source. Use zip().'>([16](#notes-16))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='At most one element per source. Use zip().'>([16](#notes-16))</sup>|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Always empty. Use merge().'>([17](#notes-17))</sup>| +<a name='wrap'></a>`wrap`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use fromPublisher().'>([110](#notes-110))</sup>|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +<a name='zip'></a>`zip`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use merge().'>([111](#notes-111))</sup>| +<a name='zipArray'></a>`zipArray`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use mergeArray().'>([112](#notes-112))</sup>| +<a name='zipWith'></a>`zipWith`|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) <sup title='Use mergeWith().'>([113](#notes-113))</sup>| +<a name='total'></a>**237 operators** | **216** | **210** | **118** | **108** | **84** | + +#### Notes +<a name='notes-1'></a><sup>1</sup> Use [`contains()`](#contains).<br/> +<a name='notes-2'></a><sup>2</sup> Always empty.<br/> +<a name='notes-3'></a><sup>3</sup> Use [`concatWith`](#concatWith).<br/> +<a name='notes-4'></a><sup>4</sup> Use [`blockingFirst()`](#blockingFirst), [`blockingSingle()`](#blockingSingle) or [`blockingLast()`](#blockingLast).<br/> +<a name='notes-5'></a><sup>5</sup> Use [`blockingGet()`](#blockingGet).<br/> +<a name='notes-6'></a><sup>6</sup> At most one element to get. Use [`blockingGet()`](#blockingGet).<br/> +<a name='notes-7'></a><sup>7</sup> No elements to get. Use [`blockingAwait()`](#blockingAwait).<br/> +<a name='notes-8'></a><sup>8</sup> Use [`blockingSubscribe()`](#blockingSubscribe)<br/> +<a name='notes-9'></a><sup>9</sup> Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.<br/> +<a name='notes-10'></a><sup>10</sup> Use [`map()`](#map) to transform into a list/collection.<br/> +<a name='notes-11'></a><sup>11</sup> Always empty. Use [`andThen()`](#andThen) to bring in a list/collection.<br/> +<a name='notes-12'></a><sup>12</sup> At most one element to store. Use [`cache()`](#cache).<br/> +<a name='notes-13'></a><sup>13</sup> At most one element to collect. Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.<br/> +<a name='notes-14'></a><sup>14</sup> One element to collect. Use [`map()`](#map) to transform into a list/collection.<br/> +<a name='notes-15'></a><sup>15</sup> Always empty. Use [`andThen()`](#andThen) to bring in a collection.<br/> +<a name='notes-16'></a><sup>16</sup> At most one element per source. Use [`zip()`](#zip).<br/> +<a name='notes-17'></a><sup>17</sup> Always empty. Use [`merge()`](#merge).<br/> +<a name='notes-18'></a><sup>18</sup> At most one element per source. Use [`zipArray()`](#zipArray).<br/> +<a name='notes-19'></a><sup>19</sup> Always empty. Use [`mergeArray()`](#mergeArray).<br/> +<a name='notes-20'></a><sup>20</sup> Always empty. Use [`mergeArrayDelayError()`](#mergeArrayDelayError).<br/> +<a name='notes-21'></a><sup>21</sup> Always empty. Use [`mergeDelayError()`](#mergeDelayError).<br/> +<a name='notes-22'></a><sup>22</sup> Use [`empty()`](#empty).<br/> +<a name='notes-23'></a><sup>23</sup> Never empty.<br/> +<a name='notes-24'></a><sup>24</sup> No items to keep ordered. Use [`mergeArray()`](#mergeArray).<br/> +<a name='notes-25'></a><sup>25</sup> No items to keep ordered. Use [`mergeArrayDelayError()`](#mergeArrayDelayError).<br/> +<a name='notes-26'></a><sup>26</sup> No items to keep ordered. Use [`merge()`](#merge).<br/> +<a name='notes-27'></a><sup>27</sup> No items to keep ordered. Use [`mergeDelayError()`](#mergeDelayError).<br/> +<a name='notes-28'></a><sup>28</sup> Always empty thus no items to map.<br/> +<a name='notes-29'></a><sup>29</sup> Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMapCompletable`](#concatMapCompletable).<br/> +<a name='notes-30'></a><sup>30</sup> Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMap`](#concatMap).<br/> +<a name='notes-31'></a><sup>31</sup> At most one item to map. Use [`concatMap()`](#concatMap).<br/> +<a name='notes-32'></a><sup>32</sup> At most one item. Use [`flattenAsFlowable`](#flattenAsFlowable) or [`flattenAsObservable`](#flattenAsObservable).<br/> +<a name='notes-33'></a><sup>33</sup> Use [`concatMap`](#concatMap).<br/> +<a name='notes-34'></a><sup>34</sup> Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMapMaybe`](#concatMapMaybe).<br/> +<a name='notes-35'></a><sup>35</sup> Use [`concatMap()`](#concatMap).<br/> +<a name='notes-36'></a><sup>36</sup> Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMapSingle`](#concatMapSingle).<br/> +<a name='notes-37'></a><sup>37</sup> At most one item. Use [`flattenStreamAsFlowable`](#flattenStreamAsFlowable) or [`flattenStreamAsObservable`](#flattenStreamAsObservable).<br/> +<a name='notes-38'></a><sup>38</sup> Never empty thus always 1.<br/> +<a name='notes-39'></a><sup>39</sup> Always empty thus always 0.<br/> +<a name='notes-40'></a><sup>40</sup> At most one item signaled so no subsequent items to work with.<br/> +<a name='notes-41'></a><sup>41</sup> Always empty thus no items to work with.<br/> +<a name='notes-42'></a><sup>42</sup> Always empty. Use [`andThen()`](#andThen) to chose the follow-up sequence.<br/> +<a name='notes-43'></a><sup>43</sup> At most one item, always distinct.<br/> +<a name='notes-44'></a><sup>44</sup> Different terminology. Use [`doAfterSuccess()`](#doAfterSuccess).<br/> +<a name='notes-45'></a><sup>45</sup> Different terminology. Use [`doAfterNext()`](#doAfterNext).<br/> +<a name='notes-46'></a><sup>46</sup> Different terminology. Use [`doOnDispose()`](#doOnDispose).<br/> +<a name='notes-47'></a><sup>47</sup> Always succeeds or fails, there is no `onComplete` signal.<br/> +<a name='notes-48'></a><sup>48</sup> Different terminology. Use [`doOnCancel()`](#doOnCancel).<br/> +<a name='notes-49'></a><sup>49</sup> At most one item. Use [`doOnEvent()`](#doOnEvent).<br/> +<a name='notes-50'></a><sup>50</sup> Use [`doOnEach()`](#doOnEach).<br/> +<a name='notes-51'></a><sup>51</sup> Different terminology. Use [`doOnSuccess()`](#doOnSuccess).<br/> +<a name='notes-52'></a><sup>52</sup> Backpressure related and not supported outside `Flowable`.<br/> +<a name='notes-53'></a><sup>53</sup> Different terminology. Use [`doOnNext()`](#doOnNext).<br/> +<a name='notes-54'></a><sup>54</sup> At most one item with index 0. Use [`defaultIfEmpty`](#defaultIfEmpty).<br/> +<a name='notes-55'></a><sup>55</sup> Always one item with index 0.<br/> +<a name='notes-56'></a><sup>56</sup> At most one item with index 0. Use [`toSingle`](#toSingle).<br/> +<a name='notes-57'></a><sup>57</sup> Use [`complete()`](#complete).<br/> +<a name='notes-58'></a><sup>58</sup> At most one item. Use [`defaultIfEmpty`](#defaultIfEmpty).<br/> +<a name='notes-59'></a><sup>59</sup> Always one item.<br/> +<a name='notes-60'></a><sup>60</sup> At most one item, would be no-op.<br/> +<a name='notes-61'></a><sup>61</sup> Always one item, would be no-op.<br/> +<a name='notes-62'></a><sup>62</sup> Always empty. Use [`andThen()`](#andThen) and [`error()`](#error).<br/> +<a name='notes-63'></a><sup>63</sup> At most one item. Use [`toCompletionStage()`](#toCompletionStage).<br/> +<a name='notes-64'></a><sup>64</sup> Always empty. Use [`andThen()`](#andThen), [`error()`](#error) and [`toCompletionStage()`](#toCompletionStage).<br/> +<a name='notes-65'></a><sup>65</sup> Use [`flatMap()`](#flatMap).<br/> +<a name='notes-66'></a><sup>66</sup> Not supported. Use [`flatMap`](#flatMap) and [`toFlowable()`](#toFlowable).<br/> +<a name='notes-67'></a><sup>67</sup> Use [`flatMap`](#flatMap).<br/> +<a name='notes-68'></a><sup>68</sup> Not supported. Use [`flatMap`](#flatMap) and [`toObservable()`](#toFlowable).<br/> +<a name='notes-69'></a><sup>69</sup> Use [`flatMapIterable()`](#flatMapIterable).<br/> +<a name='notes-70'></a><sup>70</sup> Use [`flatMapStream()`](#flatMapStream).<br/> +<a name='notes-71'></a><sup>71</sup> Use [`subscribe()`](#subscribe).<br/> +<a name='notes-72'></a><sup>72</sup> At most one item. Use [`just()`](#just) or [`empty()`](#empty).<br/> +<a name='notes-73'></a><sup>73</sup> Always one item. Use [`just()`](#just).<br/> +<a name='notes-74'></a><sup>74</sup> Always empty. Use [`complete()`](#complete).<br/> +<a name='notes-75'></a><sup>75</sup> Always error.<br/> +<a name='notes-76'></a><sup>76</sup> Use [`wrap()`](#wrap).<br/> +<a name='notes-77'></a><sup>77</sup> Use [`fromSupplier()`](#fromSupplier).<br/> +<a name='notes-78'></a><sup>78</sup> At most one item.<br/> +<a name='notes-79'></a><sup>79</sup> Always empty thus no items to group.<br/> +<a name='notes-80'></a><sup>80</sup> Always empty thus no items to join.<br/> +<a name='notes-81'></a><sup>81</sup> Use [`ignoreElements()`](#ignoreElements).<br/> +<a name='notes-82'></a><sup>82</sup> Use [`ignoreElement()`](#ignoreElement).<br/> +<a name='notes-83'></a><sup>83</sup> At most one item. Use [`timer()`](#timer).<br/> +<a name='notes-84'></a><sup>84</sup> At most one item. Use [`zip()`](#zip)<br/> +<a name='notes-85'></a><sup>85</sup> Always empty thus no items to filter.<br/> +<a name='notes-86'></a><sup>86</sup> Needs backpressure thus not supported outside `Flowable`.<br/> +<a name='notes-87'></a><sup>87</sup> Connectable sources not supported outside `Flowable` and `Observable`. Use a `MaybeSubject`.<br/> +<a name='notes-88'></a><sup>88</sup> Connectable sources not supported outside `Flowable` and `Observable`. Use a `SingleSubject`.<br/> +<a name='notes-89'></a><sup>89</sup> Connectable sources not supported outside `Flowable` and `Observable`. Use a `ConnectableSubject`.<br/> +<a name='notes-90'></a><sup>90</sup> At most one item. Use [`just()`](#just).<br/> +<a name='notes-91'></a><sup>91</sup> At most one item. Use [`map()`](#map).<br/> +<a name='notes-92'></a><sup>92</sup> Always empty thus no items to reduce.<br/> +<a name='notes-93'></a><sup>93</sup> At most one signal type.<br/> +<a name='notes-94'></a><sup>94</sup> At most one item. Use [`takeUntil()`](#takeUntil).<br/> +<a name='notes-95'></a><sup>95</sup> At most one item. Use [`filter()`](#filter).<br/> +<a name='notes-96'></a><sup>96</sup> Use [`startWith()`](#startWith) and [`fromArray()`](#fromArray) of `Flowable` or `Observable`.<br/> +<a name='notes-97'></a><sup>97</sup> Use [`startWith()`](#startWith) and [`just()`](#just) of another reactive type.<br/> +<a name='notes-98'></a><sup>98</sup> Use [`startWith()`](#startWith) and [`fromIterable()`](#fromArray) of `Flowable` or `Observable`.<br/> +<a name='notes-99'></a><sup>99</sup> Always empty. Use [`defaultIfEmpty()`](#defaultIfEmpty).<br/> +<a name='notes-100'></a><sup>100</sup> At most one item. Use [`flatMap()`](#flatMap).<br/> +<a name='notes-101'></a><sup>101</sup> Use [`firstStage`](#firstStage), [`lastStage`](#lastStage) or [`singleStage`](#singleStage).<br/> +<a name='notes-102'></a><sup>102</sup> Would be no-op.<br/> +<a name='notes-103'></a><sup>103</sup> Use [`firstElement`](#firstElement), [`lastElement`](#lastElement) or [`singleElement`](#singleElement).<br/> +<a name='notes-104'></a><sup>104</sup> Use [`firstOrError`](#firstOrError), [`lastOrError`](#lastOrError) or [`singleOrError`](#singleOrError).<br/> +<a name='notes-105'></a><sup>105</sup> Use [`first`](#first), [`last`](#last) or [`single`](#single).<br/> +<a name='notes-106'></a><sup>106</sup> Use [`defaultIfEmpty()`](#defaultIfEmpty).<br/> +<a name='notes-107'></a><sup>107</sup> Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a nested source.<br/> +<a name='notes-108'></a><sup>108</sup> Use [`map()`](#map) to transform into a nested source.<br/> +<a name='notes-109'></a><sup>109</sup> Always empty. Use [`andThen()`](#andThen) to bring in a nested source.<br/> +<a name='notes-110'></a><sup>110</sup> Use [`fromPublisher()`](#fromPublisher).<br/> +<a name='notes-111'></a><sup>111</sup> Use [`merge()`](#merge).<br/> +<a name='notes-112'></a><sup>112</sup> Use [`mergeArray()`](#mergeArray).<br/> +<a name='notes-113'></a><sup>113</sup> Use [`mergeWith()`](#mergeWith).<br/> + +#### Under development + +*Currently, all intended operators are implemented.* diff --git a/docs/Parallel-flows.md b/docs/Parallel-flows.md new file mode 100644 index 0000000000..8cd0e9a8c9 --- /dev/null +++ b/docs/Parallel-flows.md @@ -0,0 +1,33 @@ +# Introduction + +Version 2.0.5 introduced the `ParallelFlowable` API that allows parallel execution of a few select operators such as `map`, `filter`, `concatMap`, `flatMap`, `collect`, `reduce` and so on. Note that is a **parallel mode** for `Flowable` (a sub-domain specific language) instead of a new reactive base type. + +Consequently, several typical operators such as `take`, `skip` and many others are not available and there is no `ParallelObservable` because **backpressure** is essential in not flooding the internal queues of the parallel operators as by expectation, we want to go parallel because the processing of the data is slow on one thread. + +The easiest way of entering the parallel world is by using `Flowable.parallel`: + +```java +ParallelFlowable<Integer> source = Flowable.range(1, 1000).parallel(); +``` + +By default, the parallelism level is set to the number of available CPUs (`Runtime.getRuntime().availableProcessors()`) and the prefetch amount from the sequential source is set to `Flowable.bufferSize()` (128). Both can be specified via overloads of `parallel()`. + +`ParallelFlowable` follows the same principles of parametric asynchrony as `Flowable` does, therefore, `parallel()` on itself doesn't introduce the asynchronous consumption of the sequential source but only prepares the parallel flow; the asynchrony is defined via the `runOn(Scheduler)` operator. + +```java +ParallelFlowable<Integer> psource = source.runOn(Schedulers.io()); +``` + +The parallelism level (`ParallelFlowable.parallelism()`) doesn't have to match the parallelism level of the `Scheduler`. The `runOn` operator will use as many `Scheduler.Worker` instances as defined by the parallelized source. This allows `ParallelFlowable` to work for CPU intensive tasks via `Schedulers.computation()`, blocking/IO bound tasks through `Schedulers.io()` and unit testing via `TestScheduler`. You can specify the prefetch amount on `runOn` as well. + +Once the necessary parallel operations have been applied, you can return to the sequential `Flowable` via the `ParallelFlowable.sequential()` operator. + +```java +Flowable<Integer> result = psource.filter(v -> v % 3 == 0).map(v -> v * v).sequential(); +``` + +Note that `sequential` doesn't guarantee any ordering between values flowing through the parallel operators. + +# Parallel operators + +TBD \ No newline at end of file diff --git a/docs/Phantom-Operators.md b/docs/Phantom-Operators.md new file mode 100644 index 0000000000..5193147a22 --- /dev/null +++ b/docs/Phantom-Operators.md @@ -0,0 +1,166 @@ +These operators have been proposed but are not part of the 1.0 release of RxJava. + +* [**`chunkify( )`**](Phantom-Operators#chunkify) — returns an iterable that periodically returns a list of items emitted by the source Observable since the last list +* [**`fromFuture( )`**](Phantom-Operators#fromfuture) — convert a Future into an Observable, but do not attempt to get the Future's value until a Subscriber subscribes +* [**`forEachFuture( )`**](Phantom-Operators#foreachfuture) — create a futureTask that will invoke a specified function on each item emitted by an Observable +* [**`forIterable( )`**](Phantom-Operators#foriterable) — apply a function to the elements of an Iterable to create Observables which are then concatenated +* [**`fromCancellableFuture( )`, `startCancellableFuture( )`, and `deferCancellableFuture( )`**](Phantom-Operators#fromcancellablefuture-startcancellablefuture-and-defercancellablefuture-) — versions of Future-to-Observable converters that monitor the subscription status of the Observable to determine whether to halt work on the Future +* [**`generate( )` and `generateAbsoluteTime( )`**](Phantom-Operators#generate-and-generateabsolutetime) — create an Observable that emits a sequence of items as generated by a function of your choosing +* [**`groupByUntil( )`**](Phantom-Operators#groupbyuntil) — a variant of the `groupBy` operator that closes any open `GroupedObservable` upon a signal from another Observable +* [**`multicast( )`**](Phantom-Operators#multicast) — represents an Observable as a Connectable Observable +* [**`onErrorFlatMap( )`**](Phantom-Operators#onerrorflatmap) — instructs an Observable to emit a sequence of items whenever it encounters an error +* [**`parallel( )`**](Phantom-Operators#parallel) — split the work done on the emissions from an Observable into multiple Observables each operating on its own parallel thread +* [**`parallelMerge( )`**](Phantom-Operators#parallelmerge) — combine multiple Observables into a smaller number of Observables, to facilitate parallelism +* [**`pivot( )`**](Phantom-Operators#pivot) — combine multiple sets of grouped observables so that they are arranged primarily by group rather than by set +* [**`publishLast( )`**](Phantom-Operators#publishlast) — represent an Observable as a Connectable Observable that emits only the last item emitted by the source Observable + + +*** + +## chunkify( ) +#### returns an iterable that periodically returns a list of items emitted by the source Observable since the last list +<img src="/Netflix/RxJava/wiki/images/rx-operators/B.chunkify.v3.png" width="640" height="490" /> + +The `chunkify( )` operator represents a blocking observable as an Iterable, that, each time you iterate over it, returns a list of items emitted by the source Observable since the previous iteration. These lists may be empty if there have been no such items emitted. + +*** + +## fromFuture( ) +#### convert a Future into an Observable, but do not attempt to get the Future's value until a Subscriber subscribes +<img src="/Netflix/RxJava/wiki/images/rx-operators/fromFuture.v3.png" width="640" height="335" /> + +The `fromFuture( )` method also converts a Future into an Observable, but it obtains this Future indirectly, by means of a function you provide. It creates the Observable immediately, but waits to call the function and to obtain the Future until a Subscriber subscribes to it. + +*** + +## forEachFuture( ) +#### create a futureTask that will invoke a specified function on each item emitted by an Observable +<img src="/Netflix/RxJava/wiki/images/rx-operators/B.forEachFuture.v3.png" width="640" height="375" /> + +The `forEachFuture( )` returns a `FutureTask` for each item emitted by the source Observable (or each item and each notification) that, when executed, will apply a function you specify to each such item (or item and notification). + +*** + +## forIterable( ) +#### apply a function to the elements of an Iterable to create Observables which are then concatenated +<img src="/Netflix/RxJava/wiki/images/rx-operators/forIterable.v3.png" width="640" height="310" /> + +`forIterable( )` is similar to `from(Iterable )` but instead of the resulting Observable emitting the elements of the Iterable as its own emitted items, it applies a specified function to each of these elements to generate one Observable per element, and then concatenates the emissions of these Observables to be its own sequence of emitted items. + +*** + +## fromCancellableFuture( ), startCancellableFuture( ), and deferCancellableFuture( ) +#### versions of Future-to-Observable converters that monitor the subscription status of the Observable to determine whether to halt work on the Future + +If the a subscriber to the Observable that results when a Future is converted to an Observable later unsubscribes from that Observable, it can be useful to have the ability to stop attempting to retrieve items from the Future. The "cancellable" Future enables you do do this. These three methods will return Observables that, when unsubscribed to, will also "unsubscribe" from the underlying Futures. + +*** + +## generate( ) and generateAbsoluteTime( ) +#### create an Observable that emits a sequence of items as generated by a function of your choosing +<img src="/Netflix/RxJava/wiki/images/rx-operators/generate.v3.png" width="640" height="315" /> + +The basic form of `generate( )` takes four parameters. These are `initialState` and three functions: `iterate( )`, `condition( )`, and `resultSelector( )`. `generate( )` uses these four parameters to generate an Observable sequence, which is its return value. It does so in the following way. + +`generate( )` creates each emission from the sequence by applying the `resultSelector( )` function to the current _state_ and emitting the resulting item. The first state, which determines the first emitted item, is `initialState`. `generate( )` determines each subsequent state by applying `iterate( )` to the current state. Before emitting an item, `generate( )` tests the result of `condition( )` applied to the current state. If the result of this test is `false`, instead of calling `resultSelector( )` and emitting the resulting value, `generate( )` terminates the sequence and stops iterating the state. + +There are also versions of `generate( )` that allow you to do the work of generating the sequence on a particular `Scheduler` and that allow you to set the time interval between emissions by applying a function to the current state. The `generateAbsoluteTime( )` allows you to control the time at which an item is emitted by applying a function to the state to get an absolute system clock time (rather than an interval from the previous emission). + +<img src="/Netflix/RxJava/wiki/images/rx-operators/generateAbsoluteTime.v3.png" width="640" height="330" /> + +#### see also: +* <a href="http://www.introtorx.com/Content/v1.0.10621.0/04_CreatingObservableSequences.html#ObservableGenerate">Introduction to Rx: Generate</a> +* Linq: <a href="http://msdn.microsoft.com/en-us/library/system.reactive.linq.observable.generate.aspx">`Generate`</a> +* RxJS: <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md#rxobservablegenerateinitialstate-condition-iterate-resultselector-scheduler">`generate`</a>, <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md#rxobservablegeneratewithabsolutetimeinitialstate-condition-iterate-resultselector-timeselector-scheduler">`generateWithAbsoluteTime`</a>, and <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md#rxobservablegeneratewithrelativetimeinitialstate-condition-iterate-resultselector-timeselector-scheduler">`generateWithRelativeTime`</a> + +*** +## groupByUntil( ) +#### a variant of the `groupBy` operator that closes any open `GroupedObservable` upon a signal from another Observable + +This version of `groupBy` adds another parameter: an Observable that emits duration markers. When a duration marker is emitted by this Observable, any grouped Observables that have been opened are closed, and `groupByUntil( )` will create new grouped Observables for any subsequent emissions by the source Observable. + +<img src="/ReactiveX/RxJava/wiki/images/rx-operators/groupByUntil.v3.png" width="640" height="375" />​ + +Another variety of `groupByUntil( )` limits the number of groups that can be active at any particular time. If an item is emitted by the source Observable that would cause the number of groups to exceed this maximum, before the new group is emitted, one of the existing groups is closed (that is, the Observable it represents terminates by calling its Subscribers' `onCompleted` methods and then expires). + +*** + +## multicast( ) +#### represents an Observable as a Connectable Observable +To represent an Observable as a Connectable Observable, use the `multicast( )` method. + +#### see also: +* javadoc: <a href="http://reactivex.io/RxJava/javadoc/rx/Observable.html#multicast(rx.functions.Func0)">`multicast(subjectFactory)`</a> +* javadoc: <a href="http://reactivex.io/RxJava/javadoc/rx/Observable.html#multicast(rx.functions.Func0, rx.functions.Func1)">`multicast(subjectFactory, selector)`</a> +* RxJS: <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md#rxobservableprototypemulticastsubject--subjectselector-selector">`multicast`</a> +* Linq: <a href="http://msdn.microsoft.com/en-us/library/system.reactive.linq.observable.multicast.aspx">`Multicast`</a> +* <a href="http://www.introtorx.com/Content/v1.0.10621.0/14_HotAndColdObservables.html#PublishAndConnect">Introduction to Rx: Publish and Connect</a> +* <a href="http://www.introtorx.com/Content/v1.0.10621.0/14_HotAndColdObservables.html#Multicast">Introduction to Rx: Multicast</a> + +*** + +## onErrorFlatMap( ) +#### instructs an Observable to emit a sequence of items whenever it encounters an error +<img src="/Netflix/RxJava/wiki/images/rx-operators/onErrorFlatMap.v3.png" width="640" height="310" />​ + +The `onErrorFlatMap( )` method is similar to `onErrorResumeNext( )` except that it does not assume the source Observable will correctly terminate when it issues an error. Because of this, after emitting its backup sequence of items, `onErrorFlatMap( )` relinquishes control of the emitted sequence back to the source Observable. If that Observable again issues an error, `onErrorFlatMap( )` will again emit its backup sequence. + +The backup sequence is an Observable that is returned from a function that you pass to `onErrorFlatMap( )`. This function takes the Throwable issued by the source Observable as its argument, and so you can customize the sequence based on the nature of the Throwable. + +Because `onErrorFlatMap( )` is designed to work with pathological source Observables that do not terminate after issuing an error, it is mostly useful in debugging/testing scenarios. + +Note that you should apply `onErrorFlatMap( )` directly to the pathological source Observable, and not to that Observable after it has been modified by additional operators, as such operators may effectively renormalize the source Observable by unsubscribing from it immediately after it issues an error. Below, for example, is an illustration showing how `onErrorFlatMap( )` will respond to two error-generating Observables that have been merged by the `merge( )` operator. Note that it will *not* react to both errors generated by both Observables, but only to the single error passed along by `merge( )`: + +<img src="/Netflix/RxJava/wiki/images/rx-operators/onErrorFlatMap.withMerge.v3.png" width="640" height="630" />​ + +*** + +## parallel( ) +#### split the work done on the emissions from an Observable into multiple Observables each operating on its own parallel thread +<img src="/ReactiveX/RxJava/wiki/images/rx-operators/parallel.v3.png" width="640" height="475" />​ + +The `parallel( )` method splits an Observable into as many Observables as there are available processors, and does work in parallel on each of these Observables. `parallel( )` then merges the results of these parallel computations back into a single, well-behaved Observable sequence. + +For the simple “run things in parallel” use case, you can instead use something like this: +```java +streamOfItems.flatMap(item -> { + itemToObservable(item).subscribeOn(Schedulers.io()); +}); +``` +Kick off your work for each item inside [`flatMap`](Transforming-Observables#flatmap-concatmap-and-flatmapiterable) using [`subscribeOn`](Observable-Utility-Operators#subscribeon) to make it asynchronous, or by using a function that already makes asynchronous calls. + +#### see also: +* <a href="http://www.grahamlea.com/2014/07/rxjava-threading-examples/">RxJava Threading Examples</a> by Graham Lea + +*** + +## parallelMerge( ) +#### combine multiple Observables into a smaller number of Observables, to facilitate parallelism +<img src="/ReactiveX/RxJava/wiki/images/rx-operators/parallelMerge.v3.png" width="640" height="535" />​ + +Use the `parallelMerge( )` method to take an Observable that emits a large number of Observables and to reduce it to an Observable that emits a particular, smaller number of Observables that emit the same set of items as the original larger set of Observables: for instance a number of Observables that matches the number of parallel processes that you want to use when processing the emissions from the complete set of Observables. + +*** + +## pivot( ) +#### combine multiple sets of grouped observables so that they are arranged primarily by group rather than by set +<img src="/Netflix/RxJava/wiki/images/rx-operators/pivot.v3.png" width="640" height="580" />​ + +If you combine multiple sets of grouped observables, such as those created by [`groupBy( )` and `groupByUntil( )`](Transforming-Observables#wiki-groupby-and-groupbyuntil), then even if those grouped observables have been grouped by a similar differentiation function, the resulting grouping will be primarily based on which set the observable came from, not on which group the observable belonged to. + +An example may make this clearer. Imagine you use `groupBy( )` to group the emissions of an Observable (Observable1) that emits integers into two grouped observables, one emitting the even integers and the other emitting the odd integers. You then repeat this process on a second Observable (Observable2) that emits another set of integers. You hope then to combine the sets of grouped observables emitted by each of these into a single grouped Observable by means of a operator like `from(Observable1, Observable2)`. + +The result will be a grouped observable that emits two groups: the grouped observable resulting from transforming Observable1, and the grouped observable resulting from transforming Observable2. Each of those grouped observables emit observables that in turn emit the odds and evens from the source observables. You can use `pivot( )` to change this around: by applying `pivot( )` to this grouped observable it will transform into one that emits two different groups: the odds group and the evens group, with each of these groups emitting a separate observable corresponding to which source observable its set of integers came from. Here is an illustration: + +<img src="/Netflix/RxJava/wiki/images/rx-operators/pivot.ex.v3.png" width="640" height="1140" />​ + +*** + +## publishLast( ) +#### represent an Observable as a Connectable Observable that emits only the last item emitted by the source Observable +<img src="/ReactiveX/RxJava/wiki/images/rx-operators/publishLast.v3.png" width="640" height="310" /> + +#### see also: +* RxJS: <a href="https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md#rxobservableprototypepublishlatestselector">`publishLast`</a> +* Linq: <a href="http://msdn.microsoft.com/en-us/library/system.reactive.linq.observable.publishlast.aspx">`PublishLast`</a> +* <a href="http://www.introtorx.com/Content/v1.0.10621.0/14_HotAndColdObservables.html#PublishLast">Introduction to Rx: PublishLast</a> \ No newline at end of file diff --git a/docs/Plugins.md b/docs/Plugins.md new file mode 100644 index 0000000000..38dfcafd12 --- /dev/null +++ b/docs/Plugins.md @@ -0,0 +1,171 @@ +Plugins allow you to modify the default behavior of RxJava in several respects: + +* by changing the set of default computation, i/o, and new thread Schedulers +* by registering a handler for extraordinary errors that RxJava may encounter +* by registering functions that can take note of the occurrence of several regular RxJava activities + +As of 1.1.7 the regular `RxJavaPlugins` and the other hook classes have been deprecated in favor of `RxJavaHooks`. + +# RxJavaHooks + +The new `RxJavaHooks` allows you to hook into the lifecycle of the `Observable`, `Single` and `Completable` types, the `Scheduler`s returned by `Schedulers` and offers a catch-all for undeliverable errors. + +You can now change these hooks at runtime and there is no need to prepare hooks via system parameters anymore. Since users may still rely on the old hooking system, RxJavaHooks delegates to those old hooks by default. + +The `RxJavaHooks` has setters and getters of the various hook types: + +| Hook | Description | +|------|-------------| +| onError : `Action1<Throwable>` | Sets the catch-all callback | +| onObservableCreate : `Func1<Observable.OnSubscribe, Observable.OnSubscribe>` | Called when operators and sources are instantiated on `Observable` | +| onObservableStart : `Func2<Observable, Observable.OnSubscribe, Observable.OnSubscribe>` | Called before subscribing to an `Observable` actually happens | +| onObservableSubscribeError : `Func1<Throwable, Throwable>` | Called when subscribing to an `Observable` fails | +| onObservableReturn : `Func1<Subscription, Subscription>` | Called when the subscribing to an `Observable` succeeds and before returning the `Subscription` handler for it | +| onObservableLift : `Func1<Observable.Operator, Observable.Operator>` | Called when the operator `lift` is used with `Observable` | +| onSingleCreate : `Func1<Single.OnSubscribe, Single.OnSubscribe>` | Called when operators and sources are instantiated on `Single` | +| onSingleStart : `Func2<Single, Observable.OnSubscribe, Observable.OnSubscribe>` | Called before subscribing to a `Single` actually happens | +| onSingleSubscribeError : `Func1<Throwable, Throwable>` | Called when subscribing to a `Single` fails | +| onSingleReturn : `Func1<Subscription, Subscription>` | Called when the subscribing to a `Single` succeeds and before returning the `Subscription` handler for it | +| onSingleLift : `Func1<Observable.Operator, Observable.Operator>` | Called when the operator `lift` is used (note: `Observable.Operator` is deliberate here) | +| onCompletableCreate : `Func1<Completable.OnSubscribe, Completable.OnSubscribe>` | Called when operators and sources are instantiated on `Completable` | +| onCompletableStart : `Func2<Completable, Completable.OnSubscribe, Completable.OnSubscribe>` | Called before subscribing to a `Completable` actually happens | +| onCompletableSubscribeError : `Func1<Throwable, Throwable>` | Called when subscribing to a `Completable` fails | +| onCompletableLift : `Func1<Completable.Operator, Completable.Operator>` | Called when the operator `lift` is used with `Completable` | +| onComputationScheduler : `Func1<Scheduler, Scheduler>` | Called when using `Schedulers.computation()` | +| onIOScheduler : `Func1<Scheduler, Scheduler>` | Called when using `Schedulers.io()` | +| onNewThreadScheduler : `Func1<Scheduler, Scheduler>` | Called when using `Schedulers.newThread()` | +| onScheduleAction : `Func1<Action0, Action0>` | Called when a task gets scheduled in any of the `Scheduler`s | +| onGenericScheduledExecutorService : `Func0<ScheduledExecutorService>` | that should return single-threaded executors to support background timed tasks of RxJava itself | + +Reading and changing these hooks is thread-safe. + +You can also clear all hooks via `clear()` or reset to the default behavior (of delegating to the old RxJavaPlugins system) via `reset()`. + +Example: + +```java +RxJavaHooks.setOnObservableCreate(o -> { + System.out.println("Creating " + o.getClass()); + return o; +}); +try { + Observable.range(1, 10) + .map(v -> v * 2) + .filter(v -> v % 4 == 0) + .subscribe(System.out::println); +} finally { + RxJavaHooks.reset(); +} +``` + +In addition, the `RxJavaHooks` offers the so-called assembly tracking feature. This shims a custom `Observable`, `Single` and `Completable` into their chains which captures the current stacktrace when those operators were instantiated (assembly-time). Whenever an error is signalled via onError, these middle components attach this assembly-time stacktraces as last causes of that exception. This may help locating the problematic sequence in a codebase where there are too many similar flows and the plain exception itself doesn't tell which one failed in your codebase. + +Example: + +```java +RxJavaHooks.enableAssemblyTracking(); +try { + Observable.empty().single() + .subscribe(System.out::println, Throwable::printStackTrace); +} finally { + RxJavaHooks.resetAssemblyTracking(); +} +``` + +This will print something like this: + +``` +java.lang.NoSuchElementException +at rx.internal.operators.OnSubscribeSingle(OnSubscribeSingle.java:57) +... +Assembly trace: +at com.example.TrackingExample(TrackingExample:10) +``` + +The stacktrace string is also available in a field to support debugging and discovering the status of various operators in a running chain. + +The stacktrace is filtered by removing irrelevant entries such as Thread entry points, unit test runners and the entries of the tracking system itself to reduce noise. + +# RxJavaSchedulersHook + +**Deprecated** + +This plugin allows you to override the default computation, i/o, and new thread Schedulers with Schedulers of your choosing. To do this, extend the class `RxJavaSchedulersHook` and override these methods: + +* `Scheduler getComputationScheduler( )` +* `Scheduler getIOScheduler( )` +* `Scheduler getNewThreadScheduler( )` +* `Action0 onSchedule(action)` + +Then follow these steps: + +1. Create an object of the new `RxJavaDefaultSchedulers` subclass you have implemented. +1. Obtain the global `RxJavaPlugins` instance via `RxJavaPlugins.getInstance( )`. +1. Pass your default schedulers object to the `registerSchedulersHook( )` method of that instance. + +When you do this, RxJava will begin to use the Schedulers returned by your methods rather than its built-in defaults. + +# RxJavaErrorHandler + +**Deprecated** + +This plugin allows you to register a function that will handle errors that are passed to `SafeSubscriber.onError(Throwable)`. (`SafeSubscriber` is used for wrapping the incoming `Subscriber` when one calls `subscribe()`). To do this, extend the class `RxJavaErrorHandler` and override this method: + +* `void handleError(Throwable e)` + +Then follow these steps: + +1. Create an object of the new `RxJavaErrorHandler` subclass you have implemented. +1. Obtain the global `RxJavaPlugins` instance via `RxJavaPlugins.getInstance( )`. +1. Pass your error handler object to the `registerErrorHandler( )` method of that instance. + +When you do this, RxJava will begin to use your error handler to field errors that are passed to `SafeSubscriber.onError(Throwable)`. + +For example, this will call the hook: + +```java +RxJavaPlugins.getInstance().reset(); + +RxJavaPlugins.getInstance().registerErrorHandler(new RxJavaErrorHandler() { + @Override + public void handleError(Throwable e) { + e.printStackTrace(); + } +}); + +Observable.error(new IOException()) +.subscribe(System.out::println, e -> { }); +``` + +however, this call and chained operators in general won't trigger it in each stage: + +```java +Observable.error(new IOException()) +.map(v -> "" + v) +.unsafeSubscribe(System.out::println, e -> { }); +``` + +# RxJavaObservableExecutionHook + +**Deprecated** + +This plugin allows you to register functions that RxJava will call upon certain regular RxJava activities, for instance for logging or metrics-collection purposes. To do this, extend the class `RxJavaObservableExecutionHook` and override any or all of these methods: + +<table><thead> + <tr><th>method</th><th>when invoked</th></tr> + </thead><tbody> + <tr><td><tt>onCreate( )</tt></td><td>during <tt>Observable.create( )</tt></td></tr> + <tr><td><tt>onSubscribeStart( )</tt></td><td>immediately before <tt>Observable.subscribe( )</tt></td></tr> + <tr><td><tt>onSubscribeReturn( )</tt></td><td>immediately after <tt>Observable.subscribe( )</tt></td></tr> + <tr><td><tt>onSubscribeError( )</tt></td><td>upon a failed execution of <tt>Observable.subscribe( )</tt></td></tr> + <tr><td><tt>onLift( )</tt></td><td>during <tt>Observable.lift( )</tt></td></tr> + </tbody> +</table> + +Then follow these steps: + +1. Create an object of the new `RxJavaObservableExecutionHook` subclass you have implemented. +1. Obtain the global `RxJavaPlugins` instance via `RxJavaPlugins.getInstance( )`. +1. Pass your execution hooks object to the `registerObservableExecutionHook( )` method of that instance. + +When you do this, RxJava will begin to call your functions when it encounters the specific conditions they were designed to take note of. \ No newline at end of file diff --git a/docs/Problem-Solving-Examples-in-RxJava.md b/docs/Problem-Solving-Examples-in-RxJava.md new file mode 100644 index 0000000000..6b848ae693 --- /dev/null +++ b/docs/Problem-Solving-Examples-in-RxJava.md @@ -0,0 +1,130 @@ +This page will present some elementary RxJava puzzles and walk through some solutions as a way of introducing you to some of the RxJava operators. + +# Project Euler problem #1 + +There used to be a site called "Project Euler" that presented a series of mathematical computing conundrums (some fairly easy, others quite baffling) and challenged people to solve them. The first one was a sort of warm-up exercise: + +> If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000. + +There are several ways we could go about this with RxJava. We might, for instance, begin by going through all of the natural numbers below 1000 with [`range`](Creating-Observables#range) and then [`filter`](Filtering-Observables#filter) out those that are not a multiple either of 3 or of 5: +### Java +```java +Observable<Integer> threesAndFives = Observable.range(1, 999).filter(e -> e % 3 == 0 || e % 5 == 0); +``` +### Groovy +````groovy +def threesAndFives = Observable.range(1,999).filter({ !((it % 3) && (it % 5)) }); +```` +Or, we could generate two Observable sequences, one containing the multiples of three and the other containing the multiples of five (by [`map`](https://github.com/Netflix/RxJava/wiki/Transforming-Observables#map)ping each value onto its appropriate multiple), making sure to only generating new multiples while they are less than 1000 (the [`takeWhile`](Conditional-and-Boolean-Operators#takewhile-and-takewhilewithindex) operator will help here), and then [`merge`](Combining-Observables#merge) these sets: +### Java +```java +Observable<Integer> threes = Observable.range(1, 999).map(e -> e * 3).takeWhile(e -> e < 1000); +Observable<Integer> fives = Observable.range(1, 999).map(e -> e * 5).takeWhile(e -> e < 1000); +Observable<Integer> threesAndFives = Observable.merge(threes, fives).distinct(); +``` +### Groovy +````groovy +def threes = Observable.range(1,999).map({it*3}).takeWhile({it<1000}); +def fives = Observable.range(1,999).map({it*5}).takeWhile({it<1000}); +def threesAndFives = Observable.merge(threes, fives).distinct(); +```` +Don't forget the [`distinct`](Filtering-Observables#distinct) operator here, otherwise merge will duplicate numbers like 15 that are multiples of both 5 and 3. + +Next, we want to sum up the numbers in the resulting sequence. If you have installed the optional `rxjava-math` module, this is elementary: just use an operator like [`sumInteger` or `sumLong`](Mathematical-and-Aggregate-Operators#suminteger-sumlong-sumfloat-and-sumdouble) on the `threesAndFives` Observable. But what if you don't have this module? How could you use standard RxJava operators to sum up a sequence and emit that sum? + +There are a number of operators that reduce a sequence emitted by a source Observable to a single value emitted by the resulting Observable. Most of the ones that are not in the `rxjava-math` module emit boolean evaluations of the sequence; we want something that can emit a number. The [`reduce`](Mathematical-and-Aggregate-Operators#reduce) operator will do the job: +### Java +```java +Single<Integer> summer = threesAndFives.reduce(0, (a, b) -> a + b); +``` +### Groovy +````groovy +def summer = threesAndFives.reduce(0, { a, b -> a+b }); +```` +Here is how `reduce` gets the job done. It starts with 0 as a seed. Then, with each item that `threesAndFives` emits, it calls the closure `{ a, b -> a+b }`, passing it the current seed value as `a` and the emission as `b`. The closure adds these together and returns that sum, and `reduce` uses this returned value to overwrite its seed. When `threesAndFives` completes, `reduce` emits the final value returned from the closure as its sole emission: +<table> + <thead> + <tr><th>iteration</th><th>seed</th><th>emission</th><th>reduce</th></tr> + </thead> + <tbody> + <tr><td>1</td><td>0</td><td>3</td><td>3</td></tr> + <tr><td>2</td><td>3</td><td>5</td><td>8</td></tr> + <tr><td>3</td><td>8</td><td>6</td><td>14</td></tr> + <tr><td colspan="4"><center>…</center></td></tr> + <tr><td>466</td><td>232169</td><td>999</td><td>233168</td></tr> + </tbody> +</table> +Finally, we want to see the result. This means we must [subscribe](Observable#onnext-oncompleted-and-onerror) to the Observable we have constructed: + +### Java +```java +summer.subscribe(System.out::print); +``` +### Groovy +````groovy +summer.subscribe({println(it);}); +```` + +# Generate the Fibonacci Sequence + +How could you create an Observable that emits [the Fibonacci sequence](http://en.wikipedia.org/wiki/Fibonacci_number)? + +The most direct way would be to use the [`create`](Creating-Observables#wiki-create) operator to make an Observable "from scratch," and then use a traditional loop within the closure you pass to that operator to generate the sequence. Something like this: +### Java +```java +Observable<Integer> fibonacci = Observable.create(emitter -> { + int f1 = 0, f2 = 1, f = 1; + while (!emitter.isDisposed()) { + emitter.onNext(f); + f = f1 + f2; + f1 = f2; + f2 = f; + } +}); +``` +### Groovy +````groovy +def fibonacci = Observable.create({ emitter -> + def f1=0, f2=1, f=1; + while(!emitter.isDisposed()) { + emitter.onNext(f); + f = f1+f2; + f1 = f2; + f2 = f; + }; +}); +```` +But this is a little too much like ordinary linear programming. Is there some way we can instead create this sequence by composing together existing Observable operators? + +Here's an option that does this: +### Java +```java +Observable<Integer> fibonacci = + Observable.fromArray(0) + .repeat() + .scan(new int[]{0, 1}, (a, b) -> new int[]{a[1], a[0] + a[1]}) + .map(a -> a[1]); +``` +### Groovy +````groovy +def fibonacci = Observable.from(0).repeat().scan([0,1], { a,b -> [a[1], a[0]+a[1]] }).map({it[1]}); +```` +It's a little [janky](http://www.urbandictionary.com/define.php?term=janky). Let's walk through it: + +The `Observable.from(0).repeat()` creates an Observable that just emits a series of zeroes. This just serves as grist for the mill to keep [`scan`](Transforming-Observables#scan) operating. The way `scan` usually behaves is that it operates on the emissions from an Observable, one at a time, accumulating the result of operations on each emission in some sort of register, which it emits as its own emissions. The way we're using it here, it ignores the emissions from the source Observable entirely, and simply uses these emissions as an excuse to transform and emit its register. That register gets `[0,1]` as a seed, and with each iteration changes the register from `[a,b]` to `[b,a+b]` and then emits this register. + +This has the effect of emitting the following sequence of items: `[0,1], [1,1], [1,2], [2,3], [3,5], [5,8]...` + +The second item in this array describes the Fibonacci sequence. We can use `map` to reduce the sequence to just that item. + +To print out a portion of this sequence (using either method), you would use code like the following: +### Java +```java +fibonacci.take(15).subscribe(System.out::println); +``` +### Groovy +````groovy +fibonnaci.take(15).subscribe({println(it)})]; +```` + +Is there a less-janky way to do this? The [`generate`](https://github.com/Netflix/RxJava/wiki/Phantom-Operators#generate-and-generateabsolutetime) operator would avoid the silliness of creating an Observable that does nothing but turn the crank of `seed`, but this operator is not yet part of RxJava. Perhaps you can think of a more elegant solution? \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..474c8edc77 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,24 @@ +RxJava is a Java VM implementation of [ReactiveX (Reactive Extensions)](https://reactivex.io): a library for composing asynchronous and event-based programs by using observable sequences. + +For more information about ReactiveX, see the [Introduction to ReactiveX](http://reactivex.io/intro.html) page. + +### RxJava is Lightweight + +RxJava tries to be very lightweight. It is implemented as a single JAR that is focused on just the Observable abstraction and related higher-order functions. + +### RxJava is a Polyglot Implementation + +RxJava supports Java 6 or higher and JVM-based languages such as [Groovy](https://github.com/ReactiveX/RxGroovy), [Clojure](https://github.com/ReactiveX/RxClojure), [JRuby](https://github.com/ReactiveX/RxJRuby), [Kotlin](https://github.com/ReactiveX/RxKotlin) and [Scala](https://github.com/ReactiveX/RxScala). + +RxJava is meant for a more polyglot environment than just Java/Scala, and it is being designed to respect the idioms of each JVM-based language. (<a href="https://github.com/Netflix/RxJava/pull/304">This is something we’re still working on.</a>) + +### RxJava Libraries + +The following external libraries can work with RxJava: + +* [Hystrix](https://github.com/Netflix/Hystrix/wiki/How-To-Use#wiki-Reactive-Execution) latency and fault tolerance bulkheading library. +* [Camel RX](http://camel.apache.org/rx.html) provides an easy way to reuse any of the [Apache Camel components, protocols, transports and data formats](http://camel.apache.org/components.html) with the RxJava API +* [rxjava-http-tail](https://github.com/myfreeweb/rxjava-http-tail) allows you to follow logs over HTTP, like `tail -f` +* [mod-rxvertx - Extension for VertX](https://github.com/vert-x/mod-rxvertx) that provides support for Reactive Extensions (RX) using the RxJava library +* [rxjava-jdbc](https://github.com/davidmoten/rxjava-jdbc) - use RxJava with jdbc connections to stream ResultSets and do functional composition of statements +* [rtree](https://github.com/davidmoten/rtree) - immutable in-memory R-tree and R*-tree with RxJava api including backpressure \ No newline at end of file diff --git a/docs/Reactive-Streams.md b/docs/Reactive-Streams.md new file mode 100644 index 0000000000..c3a65b883a --- /dev/null +++ b/docs/Reactive-Streams.md @@ -0,0 +1,121 @@ +# Reactive Streams + RxJava + +[Reactive Streams](https://github.com/reactive-streams/reactive-streams-jvm/) has been a [collaborative effort](https://medium.com/@viktorklang/reactive-streams-1-0-0-interview-faaca2c00bec) to standardize the protocol for asynchronous streams on the JVM. The RxJava team was [part of the effort](https://github.com/reactive-streams/reactive-streams-jvm/graphs/contributors) from the beginning and supports the use of Reactive Streams APIs and eventually the [Java 9 Flow APIs](http://cs.oswego.edu/pipermail/concurrency-interest/2015-January/013641.html) which are [resulting from the success of the Reactive Stream effort](https://github.com/reactive-streams/reactive-streams-jvm/issues/195). + +## How does this relate to RxJava itself? + +#### RxJava 1.x + +Currently RxJava 1.x does not directly implement the Reactive Streams APIs. This is due to RxJava 1.x already existing and not being able to break public APIs. It does however comply semantically with the non-blocking "reactive pull" approach to backpressure and flow control and thus can use a bridge between types. The [RxJavaReactiveStreams module](https://github.com/ReactiveX/RxJavaReactiveStreams) bridges between the RxJava 1.x types and Reactive Streams types for interop between Reactive Streams implementations and passes the Reactive Streams [TCK compliance tests](https://github.com/ReactiveX/RxJavaReactiveStreams/blob/0.x/rxjava-reactive-streams/build.gradle#L8). + +Its API looks like this: + +```java +package rx; + +import org.reactivestreams.Publisher; + +public abstract class RxReactiveStreams { + + public static <T> Publisher<T> toPublisher(Observable<T> observable) { … } + + public static <T> Observable<T> toObservable(Publisher<T> publisher) { … } + +} +``` + +#### RxJava 2.x + +[RxJava 2.x](https://github.com/ReactiveX/RxJava/issues/2450) will target Reactive Streams APIs directly for Java 8+. The plan is to also support Java 9 `j.u.c.Flow` types by leveraging new Java multi-versioned jars to support this when using RxJava 2.x in Java 9 while still working on Java 8. + +RxJava 2 will truly be "Reactive Extensions" now that there is an interface to extend. RxJava 1 didn't have a base interface or contract to extend so had to define it from scratch. RxJava 2 intends on being a high performing, battle-tested, lightweight (single dependency on Reactive Streams), non-opinionated implementation of Reactive Streams and `j.u.c.Flow` that provides a library of higher-order functions with parameterized concurrency. + +## Public APIs of Libraries + +A strong area of value for Reactive Streams is public APIs exposed in libraries. Following is some guidance and recommendation on how to use both Reactive Streams and RxJava in creating reactive libraries while decoupling the concrete implementations. + +### Pros of Exposing Reactive Stream APIs instead of RxJava + +* Lightweight: Very lightweight dependency on interfaces without any concrete implementations. This keeps dependency graphs and bytesize small. +* Future Proof: Since the Reactive Stream API is so simple, was collaboratively defined and is [becoming part](https://github.com/reactive-streams/reactive-streams-jvm/issues/195) of [JDK 9](http://cs.oswego.edu/pipermail/concurrency-interest/2015-January/013641.html) it is a future proof API for exposing async access to data. The [`j.u.c.Flow` APIs](http://gee.cs.oswego.edu/dl/jsr166/dist/docs/java/util/concurrent/Flow.html) of JDK 9 match the APIs of Reactive Streams so any types that implement the Reactive Streams `Publisher` will also be able to implement the `Flow.Publisher` type. +* Interop: An API exposed with Reactive Streams types can easily be consumed by any implementation such as RxJava, Akka Streams and Reactor. + +### Cons of Exposing Reactive Stream APIs instead of RxJava + +* A Reactive Stream `Publisher` is not very useful by itself. Without higher-order functions like `flatMap` it is just a better callback. This means that consumption of a `Publisher` will almost always need to be converted or wrapped into a Reactive Stream implementation. This can be verbose and awkward to always be wrapping `Publisher` APIs into a concrete implementation. If the JVM supported extension methods this would be elegant, but since it doesn't it is explicit and verbose. + + Specifically the Reactive Streams and Flow `Publisher` interfaces do not provide any implementations of operators like `flatMap`, `merge`, `filter`, `take`, `zip` and the many others used to compose and transform async streams. A `Publisher` can only be subscribed to. A concrete implementation such as RxJava is needed to provide composition. +* The Reactive Streams specification and binary artifacts do not provide a concrete implementation of `Publisher`. Generally a library will need or want capabilities provides by RxJava, Akka Streams, etc for its internal use or just to produce a valid `Publisher` that supports backpressure semantics (which are non-trivial to implement correctly). + +### Recommended Approach + +Now that Reactive Streams has achieved 1.0 we recommend using it for core APIs that are intended for interop. This will allow embracing asynchronous stream semantics without a hard dependency on any single implementation. This means a consumer of the API can then choose RxJava 1.x, RxJava 2.x, Akka Streams, Reactor or other stream composition libraries as suits them best. It also provides better future proofing, for example as RxJava moves from 1.x to 2.x. + +However, to limit the cons listed above, we also recommend making it easy for consumption without developers needing to explicitly wrap the APIs with their composition library of choice. For this reason we recommend providing wrapper modules for popular Reactive Stream implementations on top of the core API, otherwise your customers will each need to do this themselves. + +Note that if Java offered extension methods this approach wouldn't be needed, but until Java offers that (not anytime soon if ever) the following is an approach to achieve the pros and address the cons. + +For example, a database driver may have modules such as this: + + +// core library exposing Reactive Stream Publisher APIs +* async-database-driver + +// integration jars wrapped with concrete implementations +* async-database-driver-rxjava1 +* async-database-driver-rxjava2 +* async-database-driver-akka-stream + +The "core" may expose an API like this: + +```java +package com.database.driver; + +public class Database { + public org.reactivestreams.Publisher getValue(String key); +} +``` + +The RxJava 1.x wrapper could then be a separate module that provides RxJava specific APIs like this: + +```java +package com.database.driver.rxjava1; + +public class Database { + public rx.Observable getValue(String key); +} +``` + +The core `Publisher` API can be wrapped as simply as this: + +```java +public rx.Observable getValue(String key) { + return RxReactiveStreams.toObservable(coreDatabase.getValue(key)); +} +``` + +The RxJava 2.x wrapper would differ like this (once 2.x is available): + +```java +package com.database.driver.rxjava2; + +public class Database { + public io.reactivex.Observable getValue(String key); +} +``` + +The Akka Streams wrapper would in turn look like this: + +```java +package com.database.driver.akkastream; + +public class Database { + public akka.stream.javadsl.Source getValue(String key); +} +``` + +A developer could then choose to depend directly on the `async-database-driver` APIs but most will use one of the wrappers that supports the composition library they have chosen. + +---- + +If something could be clarified further, please help improve this page via discussion at https://github.com/ReactiveX/RxJava/issues/2917 \ No newline at end of file diff --git a/docs/Scheduler.md b/docs/Scheduler.md new file mode 100644 index 0000000000..95657cc488 --- /dev/null +++ b/docs/Scheduler.md @@ -0,0 +1,3 @@ +If you want to introduce multithreading into your cascade of Observable operators, you can do so by instructing those operators (or particular Observables) to operate on particular Schedulers. + +For more information about Schedulers, see [the ReactiveX `Scheduler` documentation page](http://reactivex.io/documentation/scheduler.html). \ No newline at end of file diff --git a/docs/String-Observables.md b/docs/String-Observables.md new file mode 100644 index 0000000000..3dc3038b94 --- /dev/null +++ b/docs/String-Observables.md @@ -0,0 +1,9 @@ +The `StringObservable` class contains methods that represent operators particular to Observables that deal in string-based sequences and streams. These include: + +* [**`byLine( )`**](http://reactivex.io/documentation/operators/map.html) — converts an Observable of Strings into an Observable of Lines by treating the source sequence as a stream and splitting it on line-endings +* [**`decode( )`**](http://reactivex.io/documentation/operators/from.html) — convert a stream of multibyte characters into an Observable that emits byte arrays that respect character boundaries +* [**`encode( )`**](http://reactivex.io/documentation/operators/map.html) — transform an Observable that emits strings into an Observable that emits byte arrays that respect character boundaries of multibyte characters in the original strings +* [**`from( )`**](http://reactivex.io/documentation/operators/from.html) — convert a stream of characters or a Reader into an Observable that emits byte arrays or Strings +* [**`join( )`**](http://reactivex.io/documentation/operators/sum.html) — converts an Observable that emits a sequence of strings into an Observable that emits a single string that concatenates them all, separating them by a specified string +* [**`split( )`**](http://reactivex.io/documentation/operators/flatmap.html) — converts an Observable of Strings into an Observable of Strings that treats the source sequence as a stream and splits it on a specified regex boundary +* [**`stringConcat( )`**](http://reactivex.io/documentation/operators/sum.html) — converts an Observable that emits a sequence of strings into an Observable that emits a single string that concatenates them all \ No newline at end of file diff --git a/docs/Subject.md b/docs/Subject.md new file mode 100644 index 0000000000..ffb7feeb51 --- /dev/null +++ b/docs/Subject.md @@ -0,0 +1,12 @@ +A <a href="http://reactivex.io/RxJava/javadoc/rx/subjects/Subject.html">`Subject`</a> is a sort of bridge or proxy that acts both as an `Subscriber` and as an `Observable`. Because it is a Subscriber, it can subscribe to one or more Observables, and because it is an Observable, it can pass through the items it observes by reemitting them, and it can also emit new items. + +For more information about the varieties of Subject and how to use them, see [the ReactiveX `Subject` documentation](http://reactivex.io/documentation/subject.html). + +#### Serializing +When you use a Subject as a Subscriber, take care not to call its `onNext( )` method (or its other `on` methods) from multiple threads, as this could lead to non-serialized calls, which violates the Observable contract and creates an ambiguity in the resulting Subject. + +To protect a Subject from this danger, you can convert it into a [`SerializedSubject`](http://reactivex.io/RxJava/javadoc/rx/subjects/SerializedSubject.html) with code like the following: + +```java +mySafeSubject = new SerializedSubject( myUnsafeSubject ); +``` diff --git a/docs/The-RxJava-Android-Module.md b/docs/The-RxJava-Android-Module.md new file mode 100644 index 0000000000..ad9817c80a --- /dev/null +++ b/docs/The-RxJava-Android-Module.md @@ -0,0 +1,3 @@ +## RxAndroid + +See the [RxAndroid](https://github.com/ReactiveX/RxAndroid) project page and the [the RxAndroid wiki](https://github.com/ReactiveX/RxAndroid/wiki) for details. diff --git a/docs/Transforming-Observables.md b/docs/Transforming-Observables.md new file mode 100644 index 0000000000..3a3d94cd2e --- /dev/null +++ b/docs/Transforming-Observables.md @@ -0,0 +1,777 @@ +This page shows operators with which you can transform items that are emitted by reactive sources, such as `Observable`s. + +# Outline + +- [`buffer`](#buffer) +- [`cast`](#cast) +- [`concatMap`](#concatmap) +- [`concatMapCompletable`](#concatmapcompletable) +- [`concatMapCompletableDelayError`](#concatmapcompletabledelayerror) +- [`concatMapDelayError`](#concatmapdelayerror) +- [`concatMapEager`](#concatmapeager) +- [`concatMapEagerDelayError`](#concatmapeagerdelayerror) +- [`concatMapIterable`](#concatmapiterable) +- [`concatMapMaybe`](#concatmapmaybe) +- [`concatMapMaybeDelayError`](#concatmapmaybedelayerror) +- [`concatMapSingle`](#concatmapsingle) +- [`concatMapSingleDelayError`](#concatmapsingledelayerror) +- [`flatMap`](#flatmap) +- [`flatMapCompletable`](#flatmapcompletable) +- [`flatMapIterable`](#flatmapiterable) +- [`flatMapMaybe`](#flatmapmaybe) +- [`flatMapObservable`](#flatmapobservable) +- [`flatMapPublisher`](#flatmappublisher) +- [`flatMapSingle`](#flatmapsingle) +- [`flatMapSingleElement`](#flatmapsingleelement) +- [`flattenAsFlowable`](#flattenasflowable) +- [`flattenAsObservable`](#flattenasobservable) +- [`groupBy`](#groupby) +- [`map`](#map) +- [`scan`](#scan) +- [`switchMap`](#switchmap) +- [`window`](#window) + +## buffer + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/buffer.html](http://reactivex.io/documentation/operators/buffer.html) + +Collects the items emitted by a reactive source into buffers, and emits these buffers. + +### buffer example + +```java +Observable.range(0, 10) + .buffer(4) + .subscribe((List<Integer> buffer) -> System.out.println(buffer)); + +// prints: +// [0, 1, 2, 3] +// [4, 5, 6, 7] +// [8, 9] +``` + +## cast + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/map.html](http://reactivex.io/documentation/operators/map.html) + +Converts each item emitted by a reactive source to the specified type, and emits these items. + +### cast example + +```java +Observable<Number> numbers = Observable.just(1, 4.0, 3f, 7, 12, 4.6, 5); + +numbers.filter((Number x) -> Integer.class.isInstance(x)) + .cast(Integer.class) + .subscribe((Integer x) -> System.out.println(x)); + +// prints: +// 1 +// 7 +// 12 +// 5 +``` + +## concatMap + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a reactive source, and emits the items that result from concatenating the results of these function applications. + +### concatMap example + +```java +Observable.range(0, 5) + .concatMap(i -> { + long delay = Math.round(Math.random() * 2); + + return Observable.timer(delay, TimeUnit.SECONDS).map(n -> i); + }) + .blockingSubscribe(System.out::print); + +// prints 01234 +``` + +## concatMapCompletable + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `io.reactivex.CompletableSource`, subscribes to them one at a time and returns a `Completable` that completes when all sources completed. + +### concatMapCompletable example + +```java +Observable<Integer> source = Observable.just(2, 1, 3); +Completable completable = source.concatMapCompletable(x -> { + return Completable.timer(x, TimeUnit.SECONDS) + .doOnComplete(() -> System.out.println("Info: Processing of item \"" + x + "\" completed")); + }); + +completable.doOnComplete(() -> System.out.println("Info: Processing of all items completed")) + .blockingAwait(); + +// prints: +// Info: Processing of item "2" completed +// Info: Processing of item "1" completed +// Info: Processing of item "3" completed +// Info: Processing of all items completed +``` + +## concatMapCompletableDelayError + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `io.reactivex.CompletableSource`, subscribes to them one at a time and returns a `Completable` that completes when all sources completed. Any errors from the sources will be delayed until all of them terminate. + +### concatMapCompletableDelayError example + +```java +Observable<Integer> source = Observable.just(2, 1, 3); +Completable completable = source.concatMapCompletableDelayError(x -> { + if (x.equals(2)) { + return Completable.error(new IOException("Processing of item \"" + x + "\" failed!")); + } else { + return Completable.timer(1, TimeUnit.SECONDS) + .doOnComplete(() -> System.out.println("Info: Processing of item \"" + x + "\" completed")); + } +}); + +completable.doOnError(error -> System.out.println("Error: " + error.getMessage())) + .onErrorComplete() + .blockingAwait(); + +// prints: +// Info: Processing of item "1" completed +// Info: Processing of item "3" completed +// Error: Processing of item "2" failed! +``` + +## concatMapDelayError + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a reactive source, and emits the items that result from concatenating the results of these function applications. Any errors from the sources will be delayed until all of them terminate. + +### concatMapDelayError example + +```java +Observable.intervalRange(1, 3, 0, 1, TimeUnit.SECONDS) + .concatMapDelayError(x -> { + if (x.equals(1L)) return Observable.error(new IOException("Something went wrong!")); + else return Observable.just(x, x * x); + }) + .blockingSubscribe( + x -> System.out.println("onNext: " + x), + error -> System.out.println("onError: " + error.getMessage())); + +// prints: +// onNext: 2 +// onNext: 4 +// onNext: 3 +// onNext: 9 +// onError: Something went wrong! +``` + +## concatMapEager + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a reactive source, and emits the items that result from concatenating the results of these function applications. Unlike [`concatMap`](#concatmap), this operator eagerly subscribes to all sources. + +### concatMapEager example + +```java +Observable.range(0, 5) + .concatMapEager(i -> { + long delay = Math.round(Math.random() * 3); + + return Observable.timer(delay, TimeUnit.SECONDS) + .map(n -> i) + .doOnNext(x -> System.out.println("Info: Finished processing item " + x)); + }) + .blockingSubscribe(i -> System.out.println("onNext: " + i)); + +// prints (lines beginning with "Info..." can be displayed in a different order): +// Info: Finished processing item 2 +// Info: Finished processing item 0 +// onNext: 0 +// Info: Finished processing item 1 +// onNext: 1 +// onNext: 2 +// Info: Finished processing item 3 +// Info: Finished processing item 4 +// onNext: 3 +// onNext: 4 +``` + +## concatMapEagerDelayError + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a reactive source, and emits the items that result from concatenating the results of these function applications. A `boolean` value must be specified, which if `true` indicates that all errors from all sources will be delayed until the end, otherwise if `false`, an error from the main source will be signalled when the current source terminates. Unlike [concatMapDelayError](#concatmapdelayerror), this operator eagerly subscribes to all sources. + +### concatMapEagerDelayError example + +```java +Observable<Integer> source = Observable.create(emitter -> { + emitter.onNext(1); + emitter.onNext(2); + emitter.onError(new Error("Fatal error!")); +}); + +source.doOnError(error -> System.out.println("Info: Error from main source " + error.getMessage())) + .concatMapEagerDelayError(x -> { + return Observable.timer(1, TimeUnit.SECONDS).map(n -> x) + .doOnSubscribe(it -> System.out.println("Info: Processing of item \"" + x + "\" started")); + }, true) + .blockingSubscribe( + x -> System.out.println("onNext: " + x), + error -> System.out.println("onError: " + error.getMessage())); + +// prints: +// Info: Processing of item "1" started +// Info: Processing of item "2" started +// Info: Error from main source Fatal error! +// onNext: 1 +// onNext: 2 +// onError: Fatal error! +``` + +## concatMapIterable + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `java.lang.Iterable`, and emits the items that result from concatenating the results of these function applications. + +### concatMapIterable example + +```java +Observable.just("A", "B", "C") + .concatMapIterable(item -> List.of(item, item, item)) + .subscribe(System.out::print); + +// prints AAABBBCCC +``` + +## concatMapMaybe + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `io.reactivex.MaybeSource`, and emits the items that result from concatenating these `MaybeSource`s. + +### concatMapMaybe example + +```java +Observable.just("5", "3,14", "2.71", "FF") + .concatMapMaybe(v -> { + return Maybe.fromCallable(() -> Double.parseDouble(v)) + .doOnError(e -> System.out.println("Info: The value \"" + v + "\" could not be parsed.")) + + // Ignore values that can not be parsed. + .onErrorComplete(); + }) + .subscribe(x -> System.out.println("onNext: " + x)); + +// prints: +// onNext: 5.0 +// Info: The value "3,14" could not be parsed. +// onNext: 2.71 +// Info: The value "FF" could not be parsed. +``` + +## concatMapMaybeDelayError + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `io.reactivex.MaybeSource`, and emits the items that result from concatenating these `MaybeSource`s. Any errors from the sources will be delayed until all of them terminate. + +### concatMapMaybeDelayError example + +```java +DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd.MM.uuuu"); +Observable.just("04.03.2018", "12-08-2018", "06.10.2018", "01.12.2018") + .concatMapMaybeDelayError(date -> { + return Maybe.fromCallable(() -> LocalDate.parse(date, dateFormatter)); + }) + .subscribe( + localDate -> System.out.println("onNext: " + localDate), + error -> System.out.println("onError: " + error.getMessage())); + +// prints: +// onNext: 2018-03-04 +// onNext: 2018-10-06 +// onNext: 2018-12-01 +// onError: Text '12-08-2018' could not be parsed at index 2 +``` + +## concatMapSingle + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `io.reactivex.SingleSource`, and emits the items that result from concatenating these ``SingleSource`s. + +### concatMapSingle example + +```java +Observable.just("5", "3,14", "2.71", "FF") + .concatMapSingle(v -> { + return Single.fromCallable(() -> Double.parseDouble(v)) + .doOnError(e -> System.out.println("Info: The value \"" + v + "\" could not be parsed.")) + + // Return a default value if the given value can not be parsed. + .onErrorReturnItem(42.0); + }) + .subscribe(x -> System.out.println("onNext: " + x)); + +// prints: +// onNext: 5.0 +// Info: The value "3,14" could not be parsed. +// onNext: 42.0 +// onNext: 2.71 +// Info: The value "FF" could not be parsed. +// onNext: 42.0 +``` + +## concatMapSingleDelayError + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `io.reactivex.SingleSource`, and emits the items that result from concatenating the results of these function applications. Any errors from the sources will be delayed until all of them terminate. + +### concatMapSingleDelayError example + +```java +DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd.MM.uuuu"); +Observable.just("24.03.2018", "12-08-2018", "06.10.2018", "01.12.2018") + .concatMapSingleDelayError(date -> { + return Single.fromCallable(() -> LocalDate.parse(date, dateFormatter)); + }) + .subscribe( + localDate -> System.out.println("onNext: " + localDate), + error -> System.out.println("onError: " + error.getMessage())); + +// prints: +// onNext: 2018-03-24 +// onNext: 2018-10-06 +// onNext: 2018-12-01 +// onError: Text '12-08-2018' could not be parsed at index 2 +``` + +## flatMap + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a reactive source, and emits the items that result from merging the results of these function applications. + +### flatMap example + +```java +Observable.just("A", "B", "C") + .flatMap(a -> { + return Observable.intervalRange(1, 3, 0, 1, TimeUnit.SECONDS) + .map(b -> '(' + a + ", " + b + ')'); + }) + .blockingSubscribe(System.out::println); + +// prints (not necessarily in this order): +// (A, 1) +// (C, 1) +// (B, 1) +// (A, 2) +// (C, 2) +// (B, 2) +// (A, 3) +// (C, 3) +// (B, 3) +``` + +## flatMapCompletable + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `io.reactivex.CompletableSource`, and returns a `Completable` that completes when all sources completed. + +### flatMapCompletable example + +```java +Observable<Integer> source = Observable.just(2, 1, 3); +Completable completable = source.flatMapCompletable(x -> { + return Completable.timer(x, TimeUnit.SECONDS) + .doOnComplete(() -> System.out.println("Info: Processing of item \"" + x + "\" completed")); +}); + +completable.doOnComplete(() -> System.out.println("Info: Processing of all items completed")) + .blockingAwait(); + +// prints: +// Info: Processing of item "1" completed +// Info: Processing of item "2" completed +// Info: Processing of item "3" completed +// Info: Processing of all items completed +``` + +## flatMapIterable + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `java.lang.Iterable`, and emits the elements from these `Iterable`s. + +### flatMapIterable example + +```java +Observable.just(1, 2, 3, 4) + .flatMapIterable(x -> { + switch (x % 4) { + case 1: + return List.of("A"); + case 2: + return List.of("B", "B"); + case 3: + return List.of("C", "C", "C"); + default: + return List.of(); + } + }) + .subscribe(System.out::println); + +// prints: +// A +// B +// B +// C +// C +// C +``` + +## flatMapMaybe + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `io.reactivex.MaybeSource`, and emits the items that result from merging these `MaybeSource`s. + +### flatMapMaybe example + +```java +Observable.just(9.0, 16.0, -4.0) + .flatMapMaybe(x -> { + if (x.compareTo(0.0) < 0) return Maybe.empty(); + else return Maybe.just(Math.sqrt(x)); + }) + .subscribe( + System.out::println, + Throwable::printStackTrace, + () -> System.out.println("onComplete")); + +// prints: +// 3.0 +// 4.0 +// onComplete +``` + +## flatMapObservable + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to the item emitted by a `Maybe` or `Single`, where that function returns an `io.reactivex.ObservableSource`, and returns an `Observable` that emits the items emitted by this `ObservableSource`. + +### flatMapObservable example + +```java +Single<String> source = Single.just("Kirk, Spock, Chekov, Sulu"); +Observable<String> names = source.flatMapObservable(text -> { + return Observable.fromArray(text.split(",")) + .map(String::strip); +}); + +names.subscribe(name -> System.out.println("onNext: " + name)); + +// prints: +// onNext: Kirk +// onNext: Spock +// onNext: Chekov +// onNext: Sulu +``` + +## flatMapPublisher + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to the item emitted by a `Maybe` or `Single`, where that function returns an `org.reactivestreams.Publisher`, and returns a `Flowable` that emits the items emitted by this `Publisher`. + +### flatMapPublisher example + +```java +Single<String> source = Single.just("Kirk, Spock, Chekov, Sulu"); +Flowable<String> names = source.flatMapPublisher(text -> { + return Flowable.fromArray(text.split(",")) + .map(String::strip); +}); + +names.subscribe(name -> System.out.println("onNext: " + name)); + +// prints: +// onNext: Kirk +// onNext: Spock +// onNext: Chekov +// onNext: Sulu +``` + +## flatMapSingle + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a `io.reactivex.SingleSource`, and emits the items that result from merging these `SingleSource`s. + +### flatMapSingle example + +```java +Observable.just(4, 2, 1, 3) + .flatMapSingle(x -> Single.timer(x, TimeUnit.SECONDS).map(i -> x)) + .blockingSubscribe(System.out::print); + +// prints 1234 +``` + +*Note:* `Maybe::flatMapSingle` returns a `Single` that signals an error notification if the `Maybe` source is empty: + +```java +Maybe<Object> emptySource = Maybe.empty(); +Single<Object> result = emptySource.flatMapSingle(x -> Single.just(x)); +result.subscribe( + x -> System.out.println("onSuccess will not be printed!"), + error -> System.out.println("onError: Source was empty!")); + +// prints: +// onError: Source was empty! +``` + +Use [`Maybe::flatMapSingleElement`](#flatmapsingleelement) -- which returns a `Maybe` -- if you don't want this behaviour. + +## flatMapSingleElement + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to the item emitted by a `Maybe`, where that function returns a `io.reactivex.SingleSource`, and returns a `Maybe` that either emits the item emitted by this `SingleSource` or completes if the source `Maybe` just completes. + +### flatMapSingleElement example + +```java +Maybe<Integer> source = Maybe.just(-42); +Maybe<Integer> result = source.flatMapSingleElement(x -> { + return Single.just(Math.abs(x)); +}); + +result.subscribe(System.out::println); + +// prints 42 +``` + +## flattenAsFlowable + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to the item emitted by a `Maybe` or `Single`, where that function returns a `java.lang.Iterable`, and returns a `Flowable` that emits the elements from this `Iterable`. + +### flattenAsFlowable example + +```java +Single<Double> source = Single.just(2.0); +Flowable<Double> flowable = source.flattenAsFlowable(x -> { + return List.of(x, Math.pow(x, 2), Math.pow(x, 3)); +}); + +flowable.subscribe(x -> System.out.println("onNext: " + x)); + +// prints: +// onNext: 2.0 +// onNext: 4.0 +// onNext: 8.0 +``` + +## flattenAsObservable + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to the item emitted by a `Maybe` or `Single`, where that function returns a `java.lang.Iterable`, and returns an `Observable` that emits the elements from this `Iterable`. + +### flattenAsObservable example + +```java +Single<Double> source = Single.just(2.0); +Observable<Double> observable = source.flattenAsObservable(x -> { + return List.of(x, Math.pow(x, 2), Math.pow(x, 3)); +}); + +observable.subscribe(x -> System.out.println("onNext: " + x)); + +// prints: +// onNext: 2.0 +// onNext: 4.0 +// onNext: 8.0 +``` + +## groupBy + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/groupby.html](http://reactivex.io/documentation/operators/groupby.html) + +Groups the items emitted by a reactive source according to a specified criterion, and emits these grouped items as a `GroupedObservable` or `GroupedFlowable`. + +### groupBy example + +```java +Observable<String> animals = Observable.just( + "Tiger", "Elephant", "Cat", "Chameleon", "Frog", "Fish", "Turtle", "Flamingo"); + +animals.groupBy(animal -> animal.charAt(0), String::toUpperCase) + .concatMapSingle(Observable::toList) + .subscribe(System.out::println); + +// prints: +// [TIGER, TURTLE] +// [ELEPHANT] +// [CAT, CHAMELEON] +// [FROG, FISH, FLAMINGO] +``` + +## map + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/map.html](http://reactivex.io/documentation/operators/map.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source and emits the results of these function applications. + +### map example + +```java +Observable.just(1, 2, 3) + .map(x -> x * x) + .subscribe(System.out::println); + +// prints: +// 1 +// 4 +// 9 +``` + +## scan + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/scan.html](http://reactivex.io/documentation/operators/scan.html) + +Applies the given `io.reactivex.functions.BiFunction` to a seed value and the first item emitted by a reactive source, then feeds the result of that function application along with the second item emitted by the reactive source into the same function, and so on until all items have been emitted by the reactive source, emitting each intermediate result. + +### scan example + +```java +Observable.just(5, 3, 8, 1, 7) + .scan(0, (partialSum, x) -> partialSum + x) + .subscribe(System.out::println); + +// prints: +// 0 +// 5 +// 8 +// 16 +// 17 +// 24 +``` + +## switchMap + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/flatmap.html](http://reactivex.io/documentation/operators/flatmap.html) + +Applies the given `io.reactivex.functions.Function` to each item emitted by a reactive source, where that function returns a reactive source, and emits the items emitted by the most recently projected of these reactive sources. + +### switchMap example + +```java +Observable.interval(0, 1, TimeUnit.SECONDS) + .switchMap(x -> { + return Observable.interval(0, 750, TimeUnit.MILLISECONDS) + .map(y -> x); + }) + .takeWhile(x -> x < 3) + .blockingSubscribe(System.out::print); + +// prints 001122 +``` + +## window + +**Available in:** ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Flowable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png) `Observable`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Maybe`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Single`, ![image](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) `Completable` + +**ReactiveX documentation:** [http://reactivex.io/documentation/operators/window.html](http://reactivex.io/documentation/operators/window.html) + +Collects the items emitted by a reactive source into windows, and emits these windows as a `Flowable` or `Observable`. + +### window example + +```java +Observable.range(1, 10) + + // Create windows containing at most 2 items, and skip 3 items before starting a new window. + .window(2, 3) + .flatMapSingle(window -> { + return window.map(String::valueOf) + .reduce(new StringJoiner(", ", "[", "]"), StringJoiner::add); + }) + .subscribe(System.out::println); + +// prints: +// [1, 2] +// [4, 5] +// [7, 8] +// [10] +``` diff --git a/docs/What's-different-in-2.0.md b/docs/What's-different-in-2.0.md new file mode 100644 index 0000000000..edc681fa7f --- /dev/null +++ b/docs/What's-different-in-2.0.md @@ -0,0 +1,970 @@ +RxJava 2.0 has been completely rewritten from scratch on top of the Reactive-Streams specification. The specification itself has evolved out of RxJava 1.x and provides a common baseline for reactive systems and libraries. + +Because Reactive-Streams has a different architecture, it mandates changes to some well known RxJava types. This wiki page attempts to summarize what has changed and describes how to rewrite 1.x code into 2.x code. + +For technical details on how to write operators for 2.x, please visit the [Writing Operators](https://github.com/ReactiveX/RxJava/wiki/Writing-operators-for-2.0) wiki page. + +# Contents + + - [Maven address and base package](#maven-address-and-base-package) + - [Javadoc](#javadoc) + - [Nulls](#nulls) + - [Observable and Flowable](#observable-and-flowable) + - [Single](#single) + - [Completable](#completable) + - [Maybe](#maybe) + - [Base reactive interfaces](#base-reactive-interfaces) + - [Subjects and Processors](#subjects-and-processors) + - [Other classes](#other-classes) + - [Functional interfaces](#functional-interfaces) + - [Subscriber](#subscriber) + - [Subscription](#subscription) + - [Backpressure](#backpressure) + - [Reactive-Streams compliance](#reactive-streams-compliance) + - [Runtime hooks](#runtime-hooks) + - [Error handling](#error-handling) + - [Scheduler](#schedulers) + - [Entering the reactive world](#entering-the-reactive-world) + - [Leaving the reactive world](#leaving-the-reactive-world) + - [Testing](#testing) + - [Operator differences](#operator-differences) + - [Miscellaneous changes](#miscellaneous-changes) + + +# Maven address and base package + +To allow having RxJava 1.x and RxJava 2.x side-by-side, RxJava 2.x is under the maven coordinates `io.reactivex.rxjava2:rxjava:2.x.y` and classes are accessible below `io.reactivex`. + +Users switching from 1.x to 2.x have to re-organize their imports, but carefully. + +# Javadoc + +The official Javadoc pages for 2.x is hosted at http://reactivex.io/RxJava/2.x/javadoc/ + +# Nulls + +RxJava 2.x no longer accepts `null` values and the following will yield `NullPointerException` immediately or as a signal to downstream: + +```java +Observable.just(null); + +Single.just(null); + +Observable.fromCallable(() -> null) + .subscribe(System.out::println, Throwable::printStackTrace); + +Observable.just(1).map(v -> null) + .subscribe(System.out::println, Throwable::printStackTrace); +``` + +This means that `Observable<Void>` can no longer emit any values but only terminate normally or with an exception. API designers may instead choose to define `Observable<Object>` with no guarantee on what `Object` will be (which should be irrelevant anyway). For example, if one needs a signaller-like source, a shared enum can be defined and its solo instance `onNext`'d: + +```java +enum Irrelevant { INSTANCE; } + +Observable<Object> source = Observable.create((ObservableEmitter<Object> emitter) -> { + System.out.println("Side-effect 1"); + emitter.onNext(Irrelevant.INSTANCE); + + System.out.println("Side-effect 2"); + emitter.onNext(Irrelevant.INSTANCE); + + System.out.println("Side-effect 3"); + emitter.onNext(Irrelevant.INSTANCE); +}); + +source.subscribe(e -> { /* Ignored. */ }, Throwable::printStackTrace); +``` + +# Observable and Flowable + +A small regret about introducing backpressure in RxJava 0.x is that instead of having a separate base reactive class, the `Observable` itself was retrofitted. The main issue with backpressure is that many hot sources, such as UI events, can't be reasonably backpressured and cause unexpected `MissingBackpressureException` (i.e., beginners don't expect them). + +We try to remedy this situation in 2.x by having `io.reactivex.Observable` non-backpressured and the new `io.reactivex.Flowable` be the backpressure-enabled base reactive class. + +The good news is that operator names remain (mostly) the same. The bad news is that one should be careful when performing 'organize imports' as it may select the non-backpressured `io.reactivex.Observable` unintended. + +## Which type to use? + +When architecting dataflows (as an end-consumer of RxJava) or deciding upon what type your 2.x compatible library should take and return, you can consider a few factors that should help you avoid problems down the line such as `MissingBackpressureException` or `OutOfMemoryError`. + +### When to use Observable + + - You have a flow of no more than 1000 elements at its longest: i.e., you have so few elements over time that there is practically no chance for OOME in your application. + - You deal with GUI events such as mouse moves or touch events: these can rarely be backpressured reasonably and aren't that frequent. You may be able to handle an element frequency of 1000 Hz or less with Observable but consider using sampling/debouncing anyway. + - Your flow is essentially synchronous but your platform doesn't support Java Streams or you miss features from it. Using `Observable` has lower overhead in general than `Flowable`. *(You could also consider IxJava which is optimized for Iterable flows supporting Java 6+)*. + +### When to use Flowable + + - Dealing with 10k+ of elements that are generated in some fashion somewhere and thus the chain can tell the source to limit the amount it generates. + - Reading (parsing) files from disk is inherently blocking and pull-based which works well with backpressure as you control, for example, how many lines you read from this for a specified request amount). + - Reading from a database through JDBC is also blocking and pull-based and is controlled by you by calling `ResultSet.next()` for likely each downstream request. + - Network (Streaming) IO where either the network helps or the protocol used supports requesting some logical amount. + - Many blocking and/or pull-based data sources which may eventually get a non-blocking reactive API/driver in the future. + + +# Single + +The 2.x `Single` reactive base type, which can emit a single `onSuccess` or `onError` has been redesigned from scratch. Its architecture now derives from the Reactive-Streams design. Its consumer type (`rx.Single.SingleSubscriber<T>`) has been changed from being a class that accepts `rx.Subscription` resources to be an interface `io.reactivex.SingleObserver<T>` that has only 3 methods: + +```java +interface SingleObserver<T> { + void onSubscribe(Disposable d); + void onSuccess(T value); + void onError(Throwable error); +} +``` + +and follows the protocol `onSubscribe (onSuccess | onError)?`. + +# Completable + +The `Completable` type remains largely the same. It was already designed along the Reactive-Streams style for 1.x so no user-level changes there. + +Similar to the naming changes, `rx.Completable.CompletableSubscriber` has become `io.reactivex.CompletableObserver` with `onSubscribe(Disposable)`: + +```java +interface CompletableObserver<T> { + void onSubscribe(Disposable d); + void onComplete(); + void onError(Throwable error); +} +``` + +and still follows the protocol `onSubscribe (onComplete | onError)?`. + +# Maybe + +RxJava 2.0.0-RC2 introduced a new base reactive type called `Maybe`. Conceptually, it is a union of `Single` and `Completable` providing the means to capture an emission pattern where there could be 0 or 1 item or an error signalled by some reactive source. + +The `Maybe` class is accompanied by `MaybeSource` as its base interface type, `MaybeObserver<T>` as its signal-receiving interface and follows the protocol `onSubscribe (onSuccess | onError | onComplete)?`. Because there could be at most 1 element emitted, the `Maybe` type has no notion of backpressure (because there is no buffer bloat possible as with unknown length `Flowable`s or `Observable`s. + +This means that an invocation of `onSubscribe(Disposable)` is potentially followed by one of the other `onXXX` methods. Unlike `Flowable`, if there is only a single value to be signalled, only `onSuccess` is called and `onComplete` is not. + +Working with this new base reactive type is practically the same as the others as it offers a modest subset of the `Flowable` operators that make sense with a 0 or 1 item sequence. + +```java +Maybe.just(1) +.map(v -> v + 1) +.filter(v -> v == 1) +.defaultIfEmpty(2) +.test() +.assertResult(2); +``` + +# Base reactive interfaces + +Following the style of extending the Reactive-Streams `Publisher<T>` in `Flowable`, the other base reactive classes now extend similar base interfaces (in package `io.reactivex`): + +```java +interface ObservableSource<T> { + void subscribe(Observer<? super T> observer); +} + +interface SingleSource<T> { + void subscribe(SingleObserver<? super T> observer); +} + +interface CompletableSource { + void subscribe(CompletableObserver observer); +} + +interface MaybeSource<T> { + void subscribe(MaybeObserver<? super T> observer); +} +``` + +Therefore, many operators that required some reactive base type from the user now accept `Publisher` and `XSource`: + +```java +Flowable<R> flatMap(Function<? super T, ? extends Publisher<? extends R>> mapper); + +Observable<R> flatMap(Function<? super T, ? extends ObservableSource<? extends R>> mapper); +``` + +By having `Publisher` as input this way, you can compose with other Reactive-Streams compliant libraries without the need to wrap them or convert them into `Flowable` first. + +If an operator has to offer a reactive base type, however, the user will receive the full reactive class (as giving out an `XSource` is practically useless as it doesn't have operators on it): + +```java +Flowable<Flowable<Integer>> windows = source.window(5); + +source.compose((Flowable<T> flowable) -> + flowable + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread())); +``` + +# Subjects and Processors + +In the Reactive-Streams specification, the `Subject`-like behavior, namely being a consumer and supplier of events at the same time, is done by the `org.reactivestreams.Processor` interface. As with the `Observable`/`Flowable` split, the backpressure-aware, Reactive-Streams compliant implementations are based on the `FlowableProcessor<T>` class (which extends `Flowable` to give a rich set of instance operators). An important change regarding `Subject`s (and by extension, `FlowableProcessor`) that they no longer support `T -> R` like conversion (that is, input is of type `T` and the output is of type `R`). (We never had a use for it in 1.x and the original `Subject<T, R>` came from .NET where there is a `Subject<T>` overload because .NET allows the same class name with a different number of type arguments.) + +The `io.reactivex.subjects.AsyncSubject`, `io.reactivex.subjects.BehaviorSubject`, `io.reactivex.subjects.PublishSubject`, `io.reactivex.subjects.ReplaySubject` and `io.reactivex.subjects.UnicastSubject` in 2.x don't support backpressure (as part of the 2.x `Observable` family). + +The `io.reactivex.processors.AsyncProcessor`, `io.reactivex.processors.BehaviorProcessor`, `io.reactivex.processors.PublishProcessor`, `io.reactivex.processors.ReplayProcessor` and `io.reactivex.processors.UnicastProcessor` are backpressure-aware. The `BehaviorProcessor` and `PublishProcessor` don't coordinate requests (use `Flowable.publish()` for that) of their downstream subscribers and will signal them `MissingBackpressureException` if the downstream can't keep up. The other `XProcessor` types honor backpressure of their downstream subscribers but otherwise, when subscribed to a source (optional), they consume it in an unbounded manner (requesting `Long.MAX_VALUE`). + +## TestSubject + +The 1.x `TestSubject` has been dropped. Its functionality can be achieved via `TestScheduler`, `PublishProcessor`/`PublishSubject` and `observeOn(testScheduler)`/scheduler parameter. + +```java +TestScheduler scheduler = new TestScheduler(); +PublishSubject<Integer> ps = PublishSubject.create(); + +TestObserver<Integer> ts = ps.delay(1000, TimeUnit.MILLISECONDS, scheduler) +.test(); + +ts.assertEmpty(); + +ps.onNext(1); + +scheduler.advanceTimeBy(999, TimeUnit.MILLISECONDS); + +ts.assertEmpty(); + +scheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS); + +ts.assertValue(1); +``` + +## SerializedSubject + +The `SerializedSubject` is no longer a public class. You have to use `Subject.toSerialized()` and `FlowableProcessor.toSerialized()` instead. + +# Other classes + +The `rx.observables.ConnectableObservable` is now `io.reactivex.observables.ConnectableObservable<T>` and `io.reactivex.flowables.ConnectableFlowable<T>`. + +## GroupedObservable + +The `rx.observables.GroupedObservable` is now `io.reactivex.observables.GroupedObservable<T>` and `io.reactivex.flowables.GroupedFlowable<T>`. + +In 1.x, you could create an instance with `GroupedObservable.from()` which was used internally by 1.x. In 2.x, all use cases now extend `GroupedObservable` directly thus the factory methods are no longer available; the whole class is now abstract. + +You can extend the class and add your own custom `subscribeActual` behavior to achieve something similar to the 1.x features: + +```java +class MyGroup<K, V> extends GroupedObservable<K, V> { + final K key; + + final Subject<V> subject; + + public MyGroup(K key) { + this.key = key; + this.subject = PublishSubject.create(); + } + + @Override + public T getKey() { + return key; + } + + @Override + protected void subscribeActual(Observer<? super T> observer) { + subject.subscribe(observer); + } +} +``` + +(The same approach works with `GroupedFlowable` as well.) + +# Functional interfaces + +Because both 1.x and 2.x is aimed at Java 6+, we can't use the Java 8 functional interfaces such as `java.util.function.Function`. Instead, we defined our own functional interfaces in 1.x and 2.x follows this tradition. + +One notable difference is that all our functional interfaces now define `throws Exception`. This is a large convenience for consumers and mappers that otherwise throw and would need `try-catch` to transform or suppress a checked exception. + +```java +Flowable.just("file.txt") +.map(name -> Files.readLines(name)) +.subscribe(lines -> System.out.println(lines.size()), Throwable::printStackTrace); +``` + +If the file doesn't exist or can't be read properly, the end consumer will print out `IOException` directly. Note also the `Files.readLines(name)` invoked without try-catch. + +## Actions + +As the opportunity to reduce component count, 2.x doesn't define `Action3`-`Action9` and `ActionN` (these were unused within RxJava itself anyway). + +The remaining action interfaces were named according to the Java 8 functional types. The no argument `Action0` is replaced by the `io.reactivex.functions.Action` for the operators and `java.lang.Runnable` for the `Scheduler` methods. `Action1` has been renamed to `Consumer` and `Action2` is called `BiConsumer`. `ActionN` is replaced by the `Consumer<Object[]>` type declaration. + +## Functions + +We followed the naming convention of Java 8 by defining `io.reactivex.functions.Function` and `io.reactivex.functions.BiFunction`, plus renaming `Func3` - `Func9` into `Function3` - `Function9` respectively. The `FuncN` is replaced by the `Function<Object[], R>` type declaration. + +In addition, operators requiring a predicate no longer use `Func1<T, Boolean>` but have a separate, primitive-returning type of `Predicate<T>` (allows better inlining due to no autoboxing). + +# Subscriber + +The Reactive-Streams specification has its own Subscriber as an interface. This interface is lightweight and combines request management with cancellation into a single interface `org.reactivestreams.Subscription` instead of having `rx.Producer` and `rx.Subscription` separately. This allows creating stream consumers with less internal state than the quite heavy `rx.Subscriber` of 1.x. + +```java +Flowable.range(1, 10).subscribe(new Subscriber<Integer>() { + @Override + public void onSubscribe(Subscription s) { + s.request(Long.MAX_VALUE); + } + + @Override + public void onNext(Integer t) { + System.out.println(t); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + } + + @Override + public void onComplete() { + System.out.println("Done"); + } +}); +``` + +Due to the name conflict, replacing the package from `rx` to `org.reactivestreams` is not enough. In addition, `org.reactivestreams.Subscriber` has no notion of adding resources to it, cancelling it or requesting from the outside. + +To bridge the gap we defined abstract classes `DefaultSubscriber`, `ResourceSubscriber` and `DisposableSubscriber` (plus their `XObserver` variants) for `Flowable` (and `Observable`) respectively that offers resource tracking support (of `Disposable`s) just like `rx.Subscriber` and can be cancelled/disposed externally via `dispose()`: + +```java +ResourceSubscriber<Integer> subscriber = new ResourceSubscriber<Integer>() { + @Override + public void onStart() { + request(Long.MAX_VALUE); + } + + @Override + public void onNext(Integer t) { + System.out.println(t); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + } + + @Override + public void onComplete() { + System.out.println("Done"); + } +}; + +Flowable.range(1, 10).delay(1, TimeUnit.SECONDS).subscribe(subscriber); + +subscriber.dispose(); +``` + +Note also that due to Reactive-Streams compatibility, the method `onCompleted` has been renamed to `onComplete` without the trailing `d`. + +Since 1.x `Observable.subscribe(Subscriber)` returned `Subscription`, users often added the `Subscription` to a `CompositeSubscription` for example: + +```java +CompositeSubscription composite = new CompositeSubscription(); + +composite.add(Observable.range(1, 5).subscribe(new TestSubscriber<Integer>())); +``` + +Due to the Reactive-Streams specification, `Publisher.subscribe` returns void and the pattern by itself no longer works in 2.0. To remedy this, the method `E subscribeWith(E subscriber)` has been added to each base reactive class which returns its input subscriber/observer as is. With the two examples before, the 2.x code can now look like this since `ResourceSubscriber` implements `Disposable` directly: + +```java +CompositeDisposable composite2 = new CompositeDisposable(); + +composite2.add(Flowable.range(1, 5).subscribeWith(subscriber)); +``` + +### Calling request from onSubscribe/onStart + +Note that due to how request management works, calling `request(n)` from `Subscriber.onSubscribe` or `ResourceSubscriber.onStart` may trigger calls to `onNext` immediately before the `request()` call itself returns to the `onSubscribe`/`onStart` method of yours: + +```java +Flowable.range(1, 3).subscribe(new Subscriber<Integer>() { + + @Override + public void onSubscribe(Subscription s) { + System.out.println("OnSubscribe start"); + s.request(Long.MAX_VALUE); + System.out.println("OnSubscribe end"); + } + + @Override + public void onNext(Integer v) { + System.out.println(v); + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + } + + @Override + public void onComplete() { + System.out.println("Done"); + } +}); +``` + +This will print: + +``` +OnSubscribe start +1 +2 +3 +Done +OnSubscribe end +``` + +The problem comes when one does some initialization in `onSubscribe`/`onStart` after calling `request` there and `onNext` may or may not see the effects of the initialization. To avoid this situation, make sure you call `request` **after** all initialization have been done in `onSubscribe`/`onStart`. + +This behavior differs from 1.x where a `request` call went through a deferred logic that accumulated requests until an upstream `Producer` arrived at some time (This nature adds overhead to all operators and consumers in 1.x.) In 2.x, there is always a `Subscription` coming down first and 90% of the time there is no need to defer requesting. + +# Subscription + +In RxJava 1.x, the interface `rx.Subscription` was responsible for stream and resource lifecycle management, namely unsubscribing a sequence and releasing general resources such as scheduled tasks. The Reactive-Streams specification took this name for specifying an interaction point between a source and a consumer: `org.reactivestreams.Subscription` allows requesting a positive amount from the upstream and allows cancelling the sequence. + +To avoid the name clash, the 1.x `rx.Subscription` has been renamed into `io.reactivex.Disposable` (somewhat resembling .NET's own IDisposable). + +Because Reactive-Streams base interface, `org.reactivestreams.Publisher` defines the `subscribe()` method as `void`, `Flowable.subscribe(Subscriber)` no longer returns any `Subscription` (or `Disposable`). The other base reactive types also follow this signature with their respective subscriber types. + +The other overloads of `subscribe` now return `Disposable` in 2.x. + +The original `Subscription` container types have been renamed and updated + + - `CompositeSubscription` to `CompositeDisposable` + - `SerialSubscription` and `MultipleAssignmentSubscription` have been merged into `SerialDisposable`. The `set()` method disposes the old value and `replace()` method does not. + - `RefCountSubscription` has been removed. + +# Backpressure + +The Reactive-Streams specification mandates operators supporting backpressure, specifically via the guarantee that they don't overflow their consumers when those don't request. Operators of the new `Flowable` base reactive type now consider downstream request amounts properly, however, this doesn't mean `MissingBackpressureException` is gone. The exception is still there but this time, the operator that can't signal more `onNext` will signal this exception instead (allowing better identification of who is not properly backpressured). + +As an alternative, the 2.x `Observable` doesn't do backpressure at all and is available as a choice to switch over. + +# Reactive-Streams compliance + +**updated in 2.0.7** + +**The `Flowable`-based sources and operators are, as of 2.0.7, fully Reactive-Streams version 1.0.0 specification compliant.** + +Before 2.0.7, the operator `strict()` had to be applied in order to achieve the same level of compliance. In 2.0.7, the operator `strict()` returns `this`, is deprecated and will be removed completely in 2.1.0. + +As one of the primary goals of RxJava 2, the design focuses on performance and in order enable it, RxJava 2.0.7 adds a custom `io.reactivex.FlowableSubscriber` interface (extends `org.reactivestreams.Subscriber`) but adds no new methods to it. The new interface is **constrained to RxJava 2** and represents a consumer to `Flowable` that is able to work in a mode that relaxes the Reactive-Streams version 1.0.0 specification in rules §1.3, §2.3, §2.12 and §3.9: + + - §1.3 relaxation: `onSubscribe` may run concurrently with `onNext` in case the `FlowableSubscriber` calls `request()` from inside `onSubscribe` and it is the responsibility of `FlowableSubscriber` to ensure thread-safety between the remaining instructions in `onSubscribe` and `onNext`. + - §2.3 relaxation: calling `Subscription.cancel` and `Subscription.request` from `FlowableSubscriber.onComplete()` or `FlowableSubscriber.onError()` is considered a no-operation. + - §2.12 relaxation: if the same `FlowableSubscriber` instance is subscribed to multiple sources, it must ensure its `onXXX` methods remain thread safe. + - §3.9 relaxation: issuing a non-positive `request()` will not stop the current stream but signal an error via `RxJavaPlugins.onError`. + +From a user's perspective, if one was using the `subscribe` methods other than `Flowable.subscribe(Subscriber<? super T>)`, there is no need to do anything regarding this change and there is no extra penalty for it. + +If one was using `Flowable.subscribe(Subscriber<? super T>)` with the built-in RxJava `Subscriber` implementations such as `DisposableSubscriber`, `TestSubscriber` and `ResourceSubscriber`, there is a small runtime overhead (one `instanceof` check) associated when the code is not recompiled against 2.0.7. + +If a custom class implementing `Subscriber` was employed before, subscribing it to a `Flowable` adds an internal wrapper that ensures observing the Flowable is 100% compliant with the specification at the cost of some per-item overhead. + +In order to help lift these extra overheads, a new method `Flowable.subscribe(FlowableSubscriber<? super T>)` has been added which exposes the original behavior from before 2.0.7. It is recommended that new custom consumer implementations extend `FlowableSubscriber` instead of just `Subscriber`. + +# Runtime hooks + +The 2.x redesigned the `RxJavaPlugins` class which now supports changing the hooks at runtime. Tests that want to override the schedulers and the lifecycle of the base reactive types can do it on a case-by-case basis through callback functions. + +The class-based `RxJavaObservableHook` and friends are now gone and `RxJavaHooks` functionality is incorporated into `RxJavaPlugins`. + +# Error handling + +One important design requirement for 2.x is that no `Throwable` errors should be swallowed. This means errors that can't be emitted because the downstream's lifecycle already reached its terminal state or the downstream cancelled a sequence which was about to emit an error. + +Such errors are routed to the `RxJavaPlugins.onError` handler. This handler can be overridden with the method `RxJavaPlugins.setErrorHandler(Consumer<Throwable>)`. Without a specific handler, RxJava defaults to printing the `Throwable`'s stacktrace to the console and calls the current thread's uncaught exception handler. + +On desktop Java, this latter handler does nothing on an `ExecutorService` backed `Scheduler` and the application can keep running. However, Android is more strict and terminates the application in such uncaught exception cases. + +If this behavior is desirable can be debated, but in any case, if you want to avoid such calls to the uncaught exception handler, the **final application** that uses RxJava 2 (directly or transitively) should set a no-op handler: + +```java +// If Java 8 lambdas are supported +RxJavaPlugins.setErrorHandler(e -> { }); + +// If no Retrolambda or Jack +RxJavaPlugins.setErrorHandler(Functions.<Throwable>emptyConsumer()); +``` +It is not advised intermediate libraries change the error handler outside their own testing environment. + +Unfortunately, RxJava can't tell which of these out-of-lifecycle, undeliverable exceptions should or shouldn't crash your app. Identifying the source and reason for these exceptions can be tiresome, especially if they originate from a source and get routed to `RxJavaPlugins.onError` somewhere lower the chain. + +Therefore, 2.0.6 introduces specific exception wrappers to help distinguish and track down what was happening the time of the error: +- `OnErrorNotImplementedException`: reintroduced to detect when the user forgot to add error handling to `subscribe()`. +- `ProtocolViolationException`: indicates a bug in an operator +- `UndeliverableException`: wraps the original exception that can't be delivered due to lifecycle restrictions on a `Subscriber`/`Observer`. It is automatically applied by `RxJavaPlugins.onError` with intact stacktrace that may help find which exact operator rerouted the original error. + +If an undeliverable exception is an instance/descendant of `NullPointerException`, `IllegalStateException` (`UndeliverableException` and `ProtocolViolationException` extend this), `IllegalArgumentException`, `CompositeException`, `MissingBackpressureException` or `OnErrorNotImplementedException`, the `UndeliverableException` wrapping doesn't happen. + +In addition, some 3rd party libraries/code throw when they get interrupted by a cancel/dispose call which leads to an undeliverable exception most of the time. Internal changes in 2.0.6 now consistently cancel or dispose a `Subscription`/`Disposable` before cancelling/disposing a task or worker (which causes the interrupt on the target thread). + +``` java +// in some library +try { + doSomethingBlockingly() +} catch (InterruptedException ex) { + // check if the interrupt is due to cancellation + // if so, no need to signal the InterruptedException + if (!disposable.isDisposed()) { + observer.onError(ex); + } +} +``` + +If the library/code already did this, the undeliverable `InterruptedException`s should stop now. If this pattern was not employed before, we encourage updating the code/library in question. + +If one decides to add a non-empty global error consumer, here is an example that manages the typical undeliverable exceptions based on whether they represent a likely bug or an ignorable application/network state: + +```java +RxJavaPlugins.setErrorHandler(e -> { + if (e instanceof UndeliverableException) { + e = e.getCause(); + } + if ((e instanceof IOException) || (e instanceof SocketException)) { + // fine, irrelevant network problem or API that throws on cancellation + return; + } + if (e instanceof InterruptedException) { + // fine, some blocking code was interrupted by a dispose call + return; + } + if ((e instanceof NullPointerException) || (e instanceof IllegalArgumentException)) { + // that's likely a bug in the application + Thread.currentThread().getUncaughtExceptionHandler() + .handleException(Thread.currentThread(), e); + return; + } + if (e instanceof IllegalStateException) { + // that's a bug in RxJava or in a custom operator + Thread.currentThread().getUncaughtExceptionHandler() + .handleException(Thread.currentThread(), e); + return; + } + Log.warning("Undeliverable exception received, not sure what to do", e); +}); +``` + +# Schedulers + +The 2.x API still supports the main default scheduler types: `computation`, `io`, `newThread` and `trampoline`, accessible through `io.reactivex.schedulers.Schedulers` utility class. + +The `immediate` scheduler is not present in 2.x. It was frequently misused and didn't implement the `Scheduler` specification correctly anyway; it contained blocking sleep for delayed action and didn't support recursive scheduling at all. Use `Schedulers.trampoline()` instead. + +The `Schedulers.test()` has been removed as well to avoid the conceptional difference with the rest of the default schedulers. Those return a "global" scheduler instance whereas `test()` returned always a new instance of the `TestScheduler`. Test developers are now encouraged to simply `new TestScheduler()` in their code. + +The `io.reactivex.Scheduler` abstract base class now supports scheduling tasks directly without the need to create and then destroy a `Worker` (which is often forgotten): + +```java +public abstract class Scheduler { + + public Disposable scheduleDirect(Runnable task) { ... } + + public Disposable scheduleDirect(Runnable task, long delay, TimeUnit unit) { ... } + + public Disposable scheduleDirectPeriodically(Runnable task, long initialDelay, + long period, TimeUnit unit) { ... } + + public long now(TimeUnit unit) { ... } + + // ... rest is the same: lifecycle methods, worker creation +} +``` + +The main purpose is to avoid the tracking overhead of the `Worker`s for typically one-shot tasks. The methods have a default implementation that reuses `createWorker` properly but can be overridden with more efficient implementations if necessary. + +The method that returns the scheduler's own notion of current time, `now()` has been changed to accept a `TimeUnit` to indicate the unit of measure. + +# Entering the reactive world + +One of the design flaws of RxJava 1.x was the exposure of the `rx.Observable.create()` method that while powerful, not the typical operator you want to use to enter the reactive world. Unfortunately, so many depend on it that we couldn't remove or rename it. + +Since 2.x is a fresh start, we won't make that mistake again. Each reactive base type `Flowable`, `Observable`, `Single`, `Maybe` and `Completable` feature a safe `create` operator that does the right thing regarding backpressure (for `Flowable`) and cancellation (all): + +```java +Flowable.create((FlowableEmitter<Integer> emitter) -> { + emitter.onNext(1); + emitter.onNext(2); + emitter.onComplete(); +}, BackpressureStrategy.BUFFER); +``` + +Practically, the 1.x `fromEmitter` (formerly `fromAsync`) has been renamed to `Flowable.create`. The other base reactive types have similar `create` methods (minus the backpressure strategy). + +# Leaving the reactive world + +Apart from subscribing to the base types with their respective consumers (`Subscriber`, `Observer`, `SingleObserver`, `MaybeObserver` and `CompletableObserver`) and functional-interface based consumers (such as `subscribe(Consumer<T>, Consumer<Throwable>, Action)`), the formerly separate 1.x `BlockingObservable` (and similar classes for the others) has been integrated with the main reactive type. Now you can directly block for some results by invoking a `blockingX` operation directly: + +```java +List<Integer> list = Flowable.range(1, 100).toList().blockingGet(); // toList() returns Single + +Integer i = Flowable.range(100, 100).blockingLast(); +``` + +(The reason for this is twofold: performance and ease of use of the library as a synchronous Java 8 Streams-like processor.) + +Another significant difference between `rx.Subscriber` (and co) and `org.reactivestreams.Subscriber` (and co) is that in 2.x, your `Subscriber`s and `Observer`s are not allowed to throw anything but fatal exceptions (see `Exceptions.throwIfFatal()`). (The Reactive-Streams specification allows throwing `NullPointerException` if the `onSubscribe`, `onNext` or `onError` receives a `null` value, but RxJava doesn't let `null`s in any way.) This means the following code is no longer legal: + +```java +Subscriber<Integer> subscriber = new Subscriber<Integer>() { + @Override + public void onSubscribe(Subscription s) { + s.request(Long.MAX_VALUE); + } + + public void onNext(Integer t) { + if (t == 1) { + throw new IllegalArgumentException(); + } + } + + public void onError(Throwable e) { + if (e instanceof IllegalArgumentException) { + throw new UnsupportedOperationException(); + } + } + + public void onComplete() { + throw new NoSuchElementException(); + } +}; + +Flowable.just(1).subscribe(subscriber); +``` + +The same applies to `Observer`, `SingleObserver`, `MaybeObserver` and `CompletableObserver`. + +Since many of the existing code targeting 1.x do such things, the method `safeSubscribe` has been introduced that does handle these non-conforming consumers. + +Alternatively, you can use the `subscribe(Consumer<T>, Consumer<Throwable>, Action)` (and similar) methods to provide a callback/lambda that can throw: + +```java +Flowable.just(1) +.subscribe( + subscriber::onNext, + subscriber::onError, + subscriber::onComplete, + subscriber::onSubscribe +); +``` + +# Testing + +Testing RxJava 2.x works the same way as it does in 1.x. `Flowable` can be tested with `io.reactivex.subscribers.TestSubscriber` whereas the non-backpressured `Observable`, `Single`, `Maybe` and `Completable` can be tested with `io.reactivex.observers.TestObserver`. + +## test() "operator" + +To support our internal testing, all base reactive types now feature `test()` methods (which is a huge convenience for us) returning `TestSubscriber` or `TestObserver`: + +```java +TestSubscriber<Integer> ts = Flowable.range(1, 5).test(); + +TestObserver<Integer> to = Observable.range(1, 5).test(); + +TestObserver<Integer> tso = Single.just(1).test(); + +TestObserver<Integer> tmo = Maybe.just(1).test(); + +TestObserver<Integer> tco = Completable.complete().test(); +``` + +The second convenience is that most `TestSubscriber`/`TestObserver` methods return the instance itself allowing chaining the various `assertX` methods. The third convenience is that you can now fluently test your sources without the need to create or introduce `TestSubscriber`/`TestObserver` instance in your code: + +```java +Flowable.range(1, 5) +.test() +.assertResult(1, 2, 3, 4, 5) +; +``` + +### Notable new assert methods + + - `assertResult(T... items)`: asserts if subscribed, received exactly the given items in the given order followed by `onComplete` and no errors + - `assertFailure(Class<? extends Throwable> clazz, T... items)`: asserts if subscribed, received exactly the given items in the given order followed by a `Throwable` error of wich `clazz.isInstance()` returns true. + - `assertFailureAndMessage(Class<? extends Throwable> clazz, String message, T... items)`: same as `assertFailure` plus validates the `getMessage()` contains the specified message + - `awaitDone(long time, TimeUnit unit)` awaits a terminal event (blockingly) and cancels the sequence if the timeout elapsed. + - `assertOf(Consumer<TestSubscriber<T>> consumer)` compose some assertions into the fluent chain (used internally for fusion test as operator fusion is not part of the public API right now). + +One of the benefits is that changing `Flowable` to `Observable` here the test code part doesn't have to change at all due to the implicit type change of the `TestSubscriber` to `TestObserver`. + +## cancel and request upfront + +The `test()` method on `TestObserver` has a `test(boolean cancel)` overload which cancels/disposes the `TestSubscriber`/`TestObserver` before it even gets subscribed: + +```java +PublishSubject<Integer> pp = PublishSubject.create(); + +// nobody subscribed yet +assertFalse(pp.hasSubscribers()); + +pp.test(true); + +// nobody remained subscribed +assertFalse(pp.hasSubscribers()); +``` + +`TestSubscriber` has the `test(long initialRequest)` and `test(long initialRequest, boolean cancel)` overloads to specify the initial request amount and whether the `TestSubscriber` should be also immediately cancelled. If the `initialRequest` is given, the `TestSubscriber` offers the `requestMore(long)` method to keep requesting in a fluent manner: + +```java +Flowable.range(1, 5) +.test(0) +.assertValues() +.requestMore(1) +.assertValues(1) +.requestMore(2) +.assertValues(1, 2, 3) +.requestMore(2) +.assertResult(1, 2, 3, 4, 5); +``` + +or alternatively the `TestSubscriber` instance has to be captured to gain access to its `request()` method: + +```java +PublishProcessor<Integer> pp = PublishProcessor.create(); + +TestSubscriber<Integer> ts = pp.test(0L); + +ts.request(1); + +pp.onNext(1); +pp.onNext(2); + +ts.assertFailure(MissingBackpressureException.class, 1); +``` + +## Testing an async source + +Given an asynchronous source, fluent blocking for a terminal event is now possible: + +```java +Flowable.just(1) +.subscribeOn(Schedulers.single()) +.test() +.awaitDone(5, TimeUnit.SECONDS) +.assertResult(1); +``` + +## Mockito & TestSubscriber + +Those who are using Mockito and mocked `Observer` in 1.x has to mock the `Subscriber.onSubscribe` method to issue an initial request, otherwise, the sequence will hang or fail with hot sources: + +```java +@SuppressWarnings("unchecked") +public static <T> Subscriber<T> mockSubscriber() { + Subscriber<T> w = mock(Subscriber.class); + + Mockito.doAnswer(new Answer<Object>() { + @Override + public Object answer(InvocationOnMock a) throws Throwable { + Subscription s = a.getArgumentAt(0, Subscription.class); + s.request(Long.MAX_VALUE); + return null; + } + }).when(w).onSubscribe((Subscription)any()); + + return w; +} +``` + +# Operator differences + +Most operators are still there in 2.x and practically all of them have the same behavior as they had in 1.x. The following subsections list each base reactive type and the difference between 1.x and 2.x. + +Generally, many operators gained overloads that now allow specifying the internal buffer size or prefetch amount they should run their upstream (or inner sources). + +Some operator overloads have been renamed with a postfix, such as `fromArray`, `fromIterable` etc. The reason for this is that when the library is compiled with Java 8, the javac often can't disambiguate between functional interface types. + +Operators marked as `@Beta` or `@Experimental` in 1.x are promoted to standard. + +## 1.x Observable to 2.x Flowable + +### Factory methods: + +| 1.x | 2.x | +|----------|-----------| +| `amb` | added `amb(ObservableSource...)` overload, 2-9 argument versions dropped | +| RxRingBuffer.SIZE | `bufferSize()` | +| `combineLatest` | added varargs overload, added overloads with `bufferSize` argument, `combineLatest(List)` dropped | +| `concat` | added overload with `prefetch` argument, 5-9 source overloads dropped, use `concatArray` instead | +| N/A | added `concatArray` and `concatArrayDelayError` | +| N/A | added `concatArrayEager` and `concatArrayEagerDelayError` | +| `concatDelayError` | added overloads with option to delay till the current ends or till the very end | +| `concatEagerDelayError` | added overloads with option to delay till the current ends or till the very end | +| `create(SyncOnSubscribe)` | replaced with `generate` + overloads (distinct interfaces, you can implement them all at once) | +| `create(AsnycOnSubscribe)` | not present | +| `create(OnSubscribe)` | repurposed with safe `create(FlowableOnSubscribe, BackpressureStrategy)`, raw support via `unsafeCreate()` | +| `from` | disambiguated into `fromArray`, `fromIterable`, `fromFuture` | +| N/A | added `fromPublisher` | +| `fromAsync` | renamed to `create()` | +| N/A | added `intervalRange()` | +| `limit` | dropped, use `take` | +| `merge` | added overloads with `prefetch` | +| `mergeDelayError` | added overloads with `prefetch` | +| `sequenceEqual` | added overload with `bufferSize` | +| `switchOnNext` | added overload with `prefetch` | +| `switchOnNextDelayError` | added overload with `prefetch` | +| `timer` | deprecated overloads dropped | +| `zip` | added overloads with `bufferSize` and `delayErrors` capabilities, disambiguated to `zipArray` and `zipIterable` | + +### Instance methods: + +| 1.x | 2.x | +|----------|----------| +| `all` | **RC3** returns `Single<Boolean>` now | +| `any` | **RC3** returns `Single<Boolean>` now | +| `asObservable` | renamed to `hide()`, hides all identities now | +| `buffer` | overloads with custom `Collection` supplier | +| `cache(int)` | deprecated and dropped | +| `collect` | **RC3** returns `Single<U>` | +| `collect(U, Action2<U, T>)` | disambiguated to `collectInto` and **RC3** returns `Single<U>` | +| `concatMap` | added overloads with `prefetch` | +| `concatMapDelayError` | added overloads with `prefetch`, option to delay till the current ends or till the very end | +| `concatMapEager` | added overloads with `prefetch` | +| `concatMapEagerDelayError` | added overloads with `prefetch`, option to delay till the current ends or till the very end | `contains` | **RC3** returns `Single<Boolean> now | +| `count` | **RC3** returns `Single<Long>` now | +| `countLong` | dropped, use `count` | +| `distinct` | overload with custom `Collection` supplier. | +| `doOnCompleted` | renamed to `doOnComplete`, note the missing `d`! | +| `doOnUnsubscribe` | renamed to `Flowable.doOnCancel` and `doOnDispose` for the others, [additional info](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#dooncanceldoondisposeunsubscribeon) | +| N/A | added `doOnLifecylce` to handle `onSubscribe`, `request` and `cancel` peeking | +| `elementAt(int)` | **RC3** no longer signals NoSuchElementException if the source is shorter than the index | +| `elementAt(Func1, int)` | dropped, use `filter(predicate).elementAt(int)` | +| `elementAtOrDefault(int, T)` | renamed to `elementAt(int, T)` and **RC3** returns `Single<T>` | +| `elementAtOrDefault(Func1, int, T)` | dropped, use `filter(predicate).elementAt(int, T)` | +| `first()` | **RC3** renamed to `firstElement` and returns `Maybe<T>` | +| `first(Func1)` | dropped, use `filter(predicate).first()` | +| `firstOrDefault(T)` | renamed to `first(T)` and **RC3** returns `Single<T>` | +| `firstOrDefault(Func1, T)` | dropped, use `filter(predicate).first(T)` | +| `flatMap` | added overloads with `prefetch` | +| N/A | added `forEachWhile(Predicate<T>, [Consumer<Throwable>, [Action]])` for conditionally stopping consumption | +| `groupBy` | added overload with `bufferSize` and `delayError` option, *the custom internal map version didn't make it into RC1* | +| `ignoreElements` | **RC3** returns `Completable` | +| `isEmpty` | **RC3** returns `Single<Boolean>` | +| `last()` | **RC3** renamed to `lastElement` and returns `Maybe<T>` | +| `last(Func1)` | dropped, use `filter(predicate).last()` | +| `lastOrDefault(T)` | renamed to `last(T)` and **RC3** returns `Single<T>` | +| `lastOrDefault(Func1, T)` | dropped, use `filter(predicate).last(T)` | +| `nest` | dropped, use manual `just` | +| `publish(Func1)` | added overload with `prefetch` | +| `reduce(Func2)` | **RC3** returns `Maybe<T>` | +| N/A | added `reduceWith(Callable, BiFunction)` to reduce in a Subscriber-individual manner, returns `Single<T>` | +| N/A | added `repeatUntil(BooleanSupplier)` | +| `repeatWhen(Func1, Scheduler)` | dropped the overload, use `subscribeOn(Scheduler).repeatWhen(Function)` instead | +| `retry` | added `retry(Predicate)`, `retry(int, Predicate)` | +| N/A | added `retryUntil(BooleanSupplier)` | +| `retryWhen(Func1, Scheduler)` | dropped the overload, use `subscribeOn(Scheduler).retryWhen(Function)` instead | +| `sample` | doesn't emit the very last item if the upstream completes within the period, added overloads with `emitLast` parameter | +| N/A | added `scanWith(Callable, BiFunction)` to scan in a Subscriber-individual manner | +| `single()` | **RC3** renamed to `singleElement` and returns `Maybe<T>` | +| `single(Func1)` | dropped, use `filter(predicate).single()` | +| `singleOrDefault(T)` | renamed to `single(T)` and **RC3** returns `Single<T>` | +| `singleOrDefault(Func1, T)` | dropped, use `filter(predicate).single(T)` | +| `skipLast` | added overloads with `bufferSize` and `delayError` options | +| `startWith` | 2-9 argument version dropped, use `startWithArray` instead | +| N/A | added `startWithArray` to disambiguate | +| `subscribe` | No longer wraps all consumer types (i.e., `Observer`) with a safety wrapper, (just like the 1.x `unsafeSubscribe` no longer available). Use `safeSubscribe` to get an explicit safety wrapper around a consumer type. | +| N/A | added `subscribeWith` that returns its input after subscription | +| `switchMap` | added overload with `prefetch` argument | +| `switchMapDelayError` | added overload with `prefetch` argument | +| `takeLastBuffer` | dropped | +| N/A | added `test()` (returns TestSubscriber subscribed to this) with overloads to fluently test | +| `throttleLast` | doesn't emit the very last item if the upstream completes within the period, use `sample` with the `emitLast` parameter | +| `timeout(Func0<Observable>, ...)` | signature changed to `timeout(Publisher, ...)` and dropped the function, use `defer(Callable<Publisher>>)` if necessary | +| `toBlocking().y` | inlined as `blockingY()` operators, except `toFuture` | +| `toCompletable` | **RC3** dropped, use `ignoreElements` | +| `toList` | **RC3** returns `Single<List<T>>` | +| `toMap` | **RC3** returns `Single<Map<K, V>>` | +| `toMultimap` | **RC3** returns `Single<Map<K, Collection<V>>>` | +| N/A | added `toFuture` | +| N/A | added `toObservable` | +| `toSingle` | **RC3** dropped, use `single(T)` | +| `toSortedList` | **RC3** returns `Single<List<T>>` | +| `unsafeSubscribe` | Removed as the Reactive Streams specification mandates the `onXXX` methods don't crash and therefore the default is to not have a safety net in `subscribe`. The new `safeSubscribe` method was introduced to explicitly add the safety wrapper around a consumer type. | +| `withLatestFrom` | 5-9 source overloads dropped | +| `zipWith` | added overloads with `prefetch` and `delayErrors` options | + +### Different return types + +Some operators that produced exactly one value or an error now return `Single` in 2.x (or `Maybe` if an empty source is allowed). + +*(Remark: this is "experimental" in RC2 and RC3 to see how it feels to program with such mixed-type sequences and whether or not there has to be too much `toObservable`/`toFlowable` back-conversion.)* + +| Operator | Old return type | New return type | Remark | +|----------|-----------------|-----------------|--------| +| `all(Predicate)` | `Observable<Boolean>` | `Single<Boolean>` | Emits true if all elements match the predicate | +| `any(Predicate)` | `Observable<Boolean>` | `Single<Boolean>` | Emits true if any elements match the predicate | +| `count()` | `Observable<Long>` | `Single<Long>` | Counts the number of elements in the sequence | +| `elementAt(int)` | `Observable<T>` | `Maybe<T>` | Emits the element at the given index or completes | +| `elementAt(int, T)` | `Observable<T>` | `Single<T>` | Emits the element at the given index or the default | +| `elementAtOrError(int)` | `Observable<T>` | `Single<T>` | Emits the indexth element or a `NoSuchElementException` | +| `first(T)` | `Observable<T>` | `Single<T>` | Emits the very first element or `NoSuchElementException` | +| `firstElement()` | `Observable<T>` | `Maybe<T>` | Emits the very first element or completes | +| `firstOrError()` | `Observable<T>` | `Single<T>` | Emits the first element or a `NoSuchElementException` if the source is empty | +| `ignoreElements()` | `Observable<T>` | `Completable` | Ignore all but the terminal events | +| `isEmpty()` | `Observable<Boolean>` | `Single<Boolean>` | Emits true if the source is empty | +| `last(T)` | `Observable<T>` | `Single<T>` | Emits the very last element or the default item | +| `lastElement()` | `Observable<T>` | `Maybe<T>` | Emits the very last element or completes | +| `lastOrError()` | `Observable<T>` | `Single<T>` | Emits the lastelement or a `NoSuchElementException` if the source is empty | +| `reduce(BiFunction)` | `Observable<T>` | `Maybe<T>` | Emits the reduced value or completes | +| `reduce(Callable, BiFunction)` | `Observable<U>` | `Single<U>` | Emits the reduced value (or the initial value) | +| `reduceWith(U, BiFunction)` | `Observable<U>` | `Single<U>` | Emits the reduced value (or the initial value) | +| `single(T)` | `Observable<T>` | `Single<T>` | Emits the only element or the default item | +| `singleElement()` | `Observable<T>` | `Maybe<T>` | Emits the only element or completes | +| `singleOrError()` | `Observable<T>` | `Single<T>` | Emits the one and only element, IndexOutOfBoundsException if the source is longer than 1 item or a `NoSuchElementException` if the source is empty | +| `toList()` | `Observable<List<T>>` | `Single<List<T>>` | collects all elements into a `List` | +| `toMap()` | `Observable<Map<K, V>>` | `Single<Map<K, V>>` | collects all elements into a `Map` | +| `toMultimap()` | `Observable<Map<K, Collection<V>>>` | `Single<Map<K, Collection<V>>>` | collects all elements into a `Map` with collection | +| `toSortedList()` | `Observable<List<T>>` | `Single<List<T>>` | collects all elements into a `List` and sorts it | + +### Removals + +To make sure the final API of 2.0 is clean as possible, we remove methods and other components between release candidates without deprecating them. + +| Removed in version | Component | Remark | +|---------|-----------|--------| +| RC3 | `Flowable.toCompletable()` | use `Flowable.ignoreElements()` | +| RC3 | `Flowable.toSingle()` | use `Flowable.single(T)` | +| RC3 | `Flowable.toMaybe()` | use `Flowable.singleElement()` | +| RC3 | `Observable.toCompletable()` | use `Observable.ignoreElements()` | +| RC3 | `Observable.toSingle()` | use `Observable.single(T)` | +| RC3 | `Observable.toMaybe()` | use `Observable.singleElement()` | + +# Miscellaneous changes + +## doOnCancel/doOnDispose/unsubscribeOn + +In 1.x, the `doOnUnsubscribe` was always executed on a terminal event because 1.x' `SafeSubscriber` called `unsubscribe` on itself. This was practically unnecessary and the Reactive-Streams specification states that when a terminal event arrives at a `Subscriber`, the upstream `Subscription` should be considered cancelled and thus calling `cancel()` is a no-op. + +For the same reason, `unsubscribeOn` is not called on the regular termination path but only when there is an actual `cancel` (or `dispose`) call on the chain. + +Therefore, the following sequence won't call `doOnCancel`: + +```java +Flowable.just(1, 2, 3) +.doOnCancel(() -> System.out.println("Cancelled!")) +.subscribe(System.out::println); +``` + +However, the following will call since the `take` operator cancels after the set amount of `onNext` events have been delivered: + +```java +Flowable.just(1, 2, 3) +.doOnCancel(() -> System.out.println("Cancelled!")) +.take(2) +.subscribe(System.out::println); +``` + +If you need to perform cleanup on both regular termination or cancellation, consider the operator `using` instead. + +Alternatively, the `doFinally` operator (introduced in 2.0.1 and standardized in 2.1) calls a developer specified `Action` that gets executed after a source completed, failed with an error or got cancelled/disposed: + +```java +Flowable.just(1, 2, 3) +.doFinally(() -> System.out.println("Finally")) +.subscribe(System.out::println); + +Flowable.just(1, 2, 3) +.doFinally(() -> System.out.println("Finally")) +.take(2) // cancels the above after 2 elements +.subscribe(System.out::println); +``` diff --git a/docs/What's-different-in-3.0.md b/docs/What's-different-in-3.0.md new file mode 100644 index 0000000000..5d66960847 --- /dev/null +++ b/docs/What's-different-in-3.0.md @@ -0,0 +1,80 @@ +Table of contents + +# Introduction +TBD. + +### API signature changes + +#### as() and to() operators + +In 2.x, the `to()` operator used the generic `Function` to allow assembly-time conversion of flows into arbitrary types. The drawback of this +approach was that each base reactive type had the same `Function` interface in their method signature, +thus it was impossible to implement multiple converters for different reactive types within the same class. +To work around this issue, the `as` operator and `XConverter` interfaces have been introduced +in 2.x, which interfaces are distinct and can be implemented on the same class. Changing the signature of `to` in 2.x was not possible due +to the pledged binary compatibility of the library. + +From 3.x, the `as()` methods have been removed and the `to()` methods now each work with their respective `XConverer` interfaces: + +- `Flowable.to(Function<Flowable<T>, R>)` is now `Flowable.to(FlowableConverter<T, R>)` +- `Observable.to(Function<Observable<T>, R>)` is now `Observable.to(ObservableConverter<T, R>)` +- `Maybe.to(Function<Flowable<T>, R>)` is now `Maybe.to(MaybeConverter<T, R>)` +- `Single.to(Function<Flowable<T>, R>)` is now `Maybe.to(SingleConverter<T, R>)` +- `Completable.to(Function<Completable, R>)` is now `Completable.to(CompletableConverter<R>)` +- `ParallelFlowable.to(Function<ParallelFlowable<T>, R)` is now `ParallelFlowable.to(ParallelFlowableConverter<T, R>)` + +If one was using these methods with a lambda expression, only a recompilation is needed: + +```java +// before +source.to(flowable -> flowable.blockingFirst()); + +// after +source.to(flowable -> flowable.blockingFirst()); +``` + +If one was implementing a Function interface (typically anonymously), the interface type, type arguments and the `throws` clause have to be adjusted + +```java +// before +source.to(new Function<Flowable<Integer>, Integer>() { + @Override + public Integer apply(Flowable<Integer> t) throws Exception { + return t.blockingFirst(); + } +}); + +// after +source.to(new FlowableConverter<Integer, Integer>() { + @Override + public Integer apply(Flowable<Integer> t) { + return t.blockingFirst(); + } +}); +``` + +TBD. + +- some operators returning a more appropriate Single or Maybe +- functional interfaces throws widening to Throwable +- standard methods removed +- standard methods signature changes + +### Standardized operators + +(former experimental and beta operators from 2.x) + +TBD. + +### Operator behavior changes + +TBD. + +- connectable sources lifecycle-fixes + + +### Test support changes + +TBD. + +- methods removed from the test consumers diff --git a/docs/Writing-operators-for-2.0.md b/docs/Writing-operators-for-2.0.md new file mode 100644 index 0000000000..1a51664880 --- /dev/null +++ b/docs/Writing-operators-for-2.0.md @@ -0,0 +1,1746 @@ +##### Table of contents + + - [Introduction](#introduction) + - [Warning on internal components](#warning-on-internal-components) + - [Atomics, serialization, deferred actions](#atomics-serialization-deferred-actions) + - [Field updaters and Android](#field-updaters-and-android) + - [Request accounting](#request-accounting) + - [Once](#once) + - [Serialization](#serialization) + - [Queues](#queues) + - [Deferred actions](#deferred-actions) + - [Deferred cancellation](#deferred-cancellation) + - [Deferred requesting](#deferred-requesting) + - [Atomic error management](#atomic-error-management) + - [Half-serialization](#half-serialization) + - [Fast-path queue-drain](#fast-path-queue-drain) + - [FlowableSubscriber](#flowablesubscriber) + - [Backpressure and cancellation](#backpressure-and-cancellation) + - [Replenishing](#replenishing) + - [Stable prefetching](#stable-prefetching) + - [Single-valued results](#single-valued-results) + - [Single-element post-complete](#single-element-post-complete) + - [Multi-element post-complete](#multi-element-post-complete) + - [Creating operator classes](#creating-operator-classes) + - [Operator by extending a base reactive class](#operator-by-extending-a-base-reactive-class) + - [Operator targeting lift()](#operator-targeting-lift) + - [Operator fusion](#operator-fusion) + - [Generations](#generations) + - [Components](#components) + - [Callable and ScalarCallable](#callable-and-scalarcallable) + - [ConditionalSubscriber](#conditionalsubscriber) + - [QueueSubscription and QueueDisposable](#queuesubscription-and-queuedisposable) + - [Example implementations](#example-implementations) + - [Map-filter hybrid](https://github.com/ReactiveX/RxJava/wiki/Writing-operators-for-2.0#map--filter-hybrid) + - [Ordered merge](#ordered-merge) + +# Introduction + +Writing operators, source-like (`fromEmitter`) or intermediate-like (`flatMap`) **has always been a hard task to do in RxJava**. There are many rules to obey, many cases to consider but at the same time, many (legal) shortcuts to take to build a well performing code. Now writing an operator specifically for 2.x is 10 times harder than for 1.x. If you want to exploit all the advanced, 4th generation features, that's even 2-3 times harder on top (so 30 times harder in total). + +*(If you have been following [my blog](http://akarnokd.blogspot.hu/) about RxJava internals, writing operators is maybe only 2 times harder than 1.x; some things have moved around, some tools popped up while others have been dropped but there is a relatively straight mapping from 1.x concepts and approaches to 2.x concepts and approaches.)* + +In this article, I'll describe the how-to's from the perspective of a developer who skipped the 1.x knowledge base and basically wants to write operators that conforms the Reactive-Streams specification as well as RxJava 2.x's own extensions and additional expectations/requirements. + +Since **Reactor 3** has the same architecture as **RxJava 2** (no accident, I architected and contributed 80% of **Reactor 3** as well) the same principles outlined in this page applies to writing operators for **Reactor 3**. Note however that they chose different naming and locations for their utility and support classes so you may have to search for the equivalent components. + +## Warning on internal components + +RxJava has several hundred public classes implementing various operators and helper facilities. Since there is no way to hide these in Java 6-8, the general contract is that anything below `io.reactivex.internal` is considered private and subject to change without warnings. It is not recommended to reference these in your code (unless you contribute to RxJava itself) and must be prepared that even a patch change may shuffle/rename things around in them. That being said, they usually contain valuable tools for operator builders and as such are quite attractive to use them in your custom code. + +# Atomics, serialization, deferred actions + +As RxJava itself has building blocks for creating reactive dataflows, its components have building blocks as well in the form of concurrency primitives and algorithms. Many refer to the book [Concurrency in Practice](https://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601) for learning the fundamentals needed. Unfortunately, other than some explanation of the Java Memory Model, the book lacks the techniques required for developing operators for RxJava 1.x and 2.x. + +## Field updaters and Android + +If you looked at the source code of RxJava and then at **Reactor 3**, you might have noticed that RxJava doesn't use the +`AtomicXFieldUpdater` classes. The reason for this is that on certain Android devices, the runtime "messes up" the field +names and the reflection logic behind the field updaters fails to locate those fields in the operators. To avoid this we decided to only use the full `AtomicX` classes (as fields or extending them). + +If you target the RxJava library with your custom operator (or Android), you are encouraged to do the same. If you plan have operators running on desktop Java, feel free to use the field updaters instead. + +## Request accounting + +When dealing with backpressure in `Flowable` operators, one needs a way to account the downstream requests and emissions in response to those requests. For this we use a single `AtomicLong`. Accounting must be atomic because requesting more and emitting items to fulfill an earlier request may happen at the same time. + +The naive approach for accounting would be to simply call `AtomicLong.getAndAdd()` with new requests and `AtomicLong.addAndGet()` for decrementing based on how many elements were emitted. + +The problem with this is that the Reactive-Streams specification declares `Long.MAX_VALUE` as the upper bound for outstanding requests (interprets it as the unbounded mode) but adding two large longs may overflow the `long` into a negative value. In addition, if for some reason, there are more values emitted than were requested, the subtraction may yield a negative current request value, causing crashes or hangs. + +Therefore, both addition and subtraction have to be capped at `Long.MAX_VALUE` and `0` respectively. Since there is no dedicated `AtomicLong` method for it, we have to use a Compare-And-Set loop. (Usually, requesting happens relatively rarely compared to emission amounts thus the lack of dedicated machine code instruction is not a performance bottleneck.) + +```java +public static long add(AtomicLong requested, long n) { + for (;;) { + long current = requested.get(); + if (current == Long.MAX_VALUE) { + return Long.MAX_VALUE; + } + long update = current + n; + if (update < 0L) { + update = Long.MAX_VALUE; + } + if (requested.compareAndSet(current, update)) { + return current; + } + } +} + +public static long produced(AtomicLong requested, long n) { +for (;;) { + long current = requested.get(); + if (current == Long.MAX_VALUE) { + return Long.MAX_VALUE; + } + long update = current - n; + if (update < 0L) { + update = 0; + } + if (requested.compareAndSet(current, update)) { + return update; + } + } +} +``` + +In fact, these are so common in RxJava's operators that these algorithms are available as utility methods on the **internal** `BackpressureHelper` class under the same name. + +Sometimes, instead of having a separate `AtomicLong` field, your operator can extend `AtomicLong` saving on the indirection and class size. The practice in RxJava 2.x operators is that unless there is another atomic counter needed by the operator, (such as work-in-progress counter, see the later subsection) and otherwise doesn't need a base class, they extend `AtomicLong` directly. + +The `BackpressureHelper` class features special versions of `add` and `produced` which treat `Long.MIN_VALUE` as a cancellation indication and won't change the `AtomicLong`s value if they see it. + +## Once + +RxJava 2 expanded the single-event reactive types to include `Maybe` (called as the reactive `Optional` by some). The common property of `Single`, `Completable` and `Maybe` is that they can only call one of the 3 kinds of methods on their consumers: `(onSuccess | onError | onComplete)`. Since they also participate in concurrent scenarios, an operator needs a way to ensure that only one of them is called even though the input sources may call multiple of them at once. + +To ensure this, operators may use the `AtomicBoolean.compareAndSet` to atomically chose the event to relay (and thus the other events to drop). + +```java +final AtomicBoolean once = new AtomicBoolean(); + +final MaybeObserver<? super T> child = ...; + +void emitSuccess(T value) { + if (once.compareAndSet(false, true)) { + child.onSuccess(value); + } +} + +void emitFailure(Throwable e) { + if (once.compareAndSet(false, true)) { + child.onError(e); + } else { + RxJavaPlugins.onError(e); + } +} +``` + +Note that the same sequential requirement applies to these 0-1 reactive sources as to `Flowable`/`Observable`, therefore, if your operator doesn't have to deal with events from multiple sources (and pick one of them), you don't need this construct. + +## Serialization + +With more complicated sources, it may happen that multiple things happen that may trigger emission towards the downstream, such as upstream becoming available while the downstream requests for more data while the sequence gets cancelled by a timeout. + +Instead of working out the often very complicated state transitions via atomics, perhaps the easiest way is to serialize the events, actions or tasks and have one thread perform the necessary steps after that. This is what I call **queue-drain** approach (or trampolining by some). + +(The other approach, **emitter-loop** is no longer recommended with 2.x due to its potential blocking `synchronized` constructs that looks performant in single-threaded case but destroys it in true concurrent case.) + + +The concept is relatively simple: have a concurrent queue and a work-in-progress atomic counter, enqueue the item, increment the counter and if the counter transitioned from 0 to 1, keep draining the queue, work with the element and decrement the counter until it reaches zero again: + +```java +final ConcurrentLinkedQueue<Runnable> queue = ...; +final AtomicInteger wip = ...; + +void execute(Runnable r) { + queue.offer(r); + if (wip.getAndIncrement() == 0) { + do { + queue.poll().run(); + } while (wip.decrementAndGet() != 0); + } +} +``` + +The same pattern applies when one has to emit onNext values to a downstream consumer: + +```java +final ConcurrentLinkedQueue<T> queue = ...; +final AtomicInteger wip = ...; +final Subscriber<? super T> child = ...; + +void emit(T r) { + queue.offer(r); + if (wip.getAndIncrement() == 0) { + do { + child.onNext(queue.poll()); + } while (wip.decrementAndGet() != 0); + } +} +``` + +### Queues + +Using `ConcurrentLinkedQueue` is a reliable although mostly an overkill for such situations because it allocates on each call to `offer()` and is unbounded. It can be replaced with more optimized queues (see [JCTools](https://github.com/JCTools/JCTools/)) and RxJava itself also has some customized queues available (internal!): + + - `SpscArrayQueue` used when the queue is known to be fed by a single thread but the serialization has to look at other things (request, cancellation, termination) that can be read from other fields. Example: `observeOn` has a fixed request pattern which fits into this type of queue and extra fields for passing an error, completion or downstream requests into the drain logic. + - `SpscLinkedArrayQueue` used when the queue is known to be fed by a single thread but there is no bound on the element count. Example: `UnicastProcessor`, almost all buffering `Observable` operator. Some operators use it with multiple event sources by synchronizing on the `offer` side - a tradeoff between allocation and potential blocking: + +```java +SpscLinkedArrayQueue<T> q = ... +synchronized(q) { + q.offer(value); +} +``` + + - `MpscLinkedQueue` where there could be many feeders and unknown number of elements. Example: `buffer` with reactive boundary. + +The RxJava 2.x implementations of these types of queues have different class hierarchy than the JDK/JCTools versions. Our classes don't implement the `java.util.Queue` interface but rather a custom, simplified interface: + +```java +interface SimpleQueue<T> { + boolean offer(T t); + + boolean offer(T t1, T t2); + + T poll() throws Exception; + + boolean isEmpty(); + + void clear(); +} + +interface SimplePlainQueue<T> extends SimpleQueue<T> { + @Override + T poll(); +} + +public final class SpscArrayQueue<T> implements SimplePlainQueue<T> { + // ... +} +``` + +This simplified queue API gets rid of the unused parts (iterator, collections API remnants) and adds a bi-offer method (only implemented atomically in `SpscLinkedArrayQueue` currently). The second interface, `SimplePlainQueue` is defined to suppress the `throws Exception` on poll on queue types that won't throw that exception and there is no need for try-catch around them. + +## Deferred actions + +The Reactive-Streams has a strict requirement that calling `onSubscribe()` must happen before any calls to the rest of the `onXXX` methods and by nature, any calls to `Subscription.request()` and `Subscription.cancel()`. The same logic applies to the design of `Observable`, `Single`, `Completable` and `Maybe` with their connection type of `Disposable`. + +Often though, such call to `onSubscribe` may happen later than the respective `cancel()` needs to happen. For example, the user may want to call `cancel()` before the respective `Subscription` actually becomes available in `subscribeOn`. Other operators may need to call `onSubscribe` before they connect to other sources but at that time, there is no direct way for relaying a `cancel` call to an unavailable upstream `Subscription`. + +The solution is **deferred cancellation** and **deferred requesting** in general. + +### Deferred cancellation + +This approach affects all 5 reactive types and works the same way for everyone. First, have an `AtomicReference` that will hold the respective connection type (or any other type whose method call has to happen later). Two methods are needed handling the `AtomicReference` class, one that sets the actual instance and one that calls the `cancel`/`dispose` method on it. + +```java +static final Disposable DISPOSED; +static { + DISPOSED = Disposables.empty(); + DISPOSED.dispose(); +} + +static boolean set(AtomicReference<Disposable> target, Disposable value) { + for (;;) { + Disposable current = target.get(); + if (current == DISPOSED) { + if (value != null) { + value.dispose(); + } + return false; + } + if (target.compareAndSet(current, value)) { + if (current != null) { + current.dispose(); + } + return true; + } + } +} + +static boolean dispose(AtomicReference<Disposable> target) { + Disposable current = target.getAndSet(DISPOSED); + if (current != DISPOSED) { + if (current != null) { + current.dispose(); + } + return true; + } + return false; +} +``` + +The approach uses an unique sentinel value `DISPOSED` - that should not appear elsewhere in your code - to indicate once a late actual `Disposable` arrives, it should be disposed immediately. Both methods return true if the operation succeeded or false if the target was already disposed. + +Sometimes, only one call to `set` is permitted (i.e., `setOnce`) and other times, the previous non-null value needs no call to `dispose` because it is known to be disposed already (i.e., `replace`). + +As with the request management, there are utility classes and methods for these operations: + + - (internal) `SequentialDisposable` that uses `update`, `replace` and `dispose` but leaks the API of `AtomicReference` + - `SerialDisposable` that has safe API with `set`, `replace` and `dispose` among other things + - (internal) `DisposableHelper` that features the methods shown above and the global disposed sentinel used by RxJava. It may come handy when one uses `AtomicReference<Disposable>` as a base class. + +The same pattern applies to `Subscription` with its `cancel()` method and with helper (internal) class `SubscriptionHelper` (but no `SequentialSubscription` or `SerialSubscription`, see next subsection). + +### Deferred requesting + +With `Flowable`s (and Reactive-Streams `Publisher`s) the `request()` calls need to be deferred as well. In one form (the simpler one), the respective late `Subscription` will eventually arrive and we need to relay all previous and all subsequent request amount to its `request()` method. + +In 1.x, this behavior was implicitly provided by `rx.Subscriber` but at a high cost that had to be payed by all instances whether or not they needed this feature. + +The solution works by having the `AtomicReference` for the `Subscription` and an `AtomicLong` to store and accumulate the requests until the actual `Subscription` arrives, then atomically request all deferred value once. + +```java +static boolean deferredSetOnce(AtomicReference<Subscription> subscription, + AtomicLong requested, Subscription newSubscription) { + if (subscription.compareAndSet(null, newSubscription) { + long r = requested.getAndSet(0L); + if (r != 0) { + newSubscription.request(r); + } + return true; + } + newSubscription.cancel(); + if (subscription.get() != SubscriptionHelper.CANCELLED) { + RxJavaPlugins.onError(new IllegalStateException("Subscription already set!")); + } + return false; +} + +static void deferredRequest(AtomicReference<Subscription> subscription, + AtomicLong requested, long n) { + Subscription current = subscription.get(); + if (current != null) { + current.request(n); + } else { + BackpressureHelper.add(requested, n); + current = subscription.get(); + if (current != null) { + long r = requested.getAndSet(0L); + if (r != 0L) { + current.request(r); + } + } + } +} +``` + +In `deferredSetOnce`, if the CAS from null to the `newSubscription` succeeds, we atomically exchange the request amount to 0L and if the original value was nonzero, we request from `newSubscription`. In `deferredRequest`, if there is a `Subscription` we simply request from it directly. Otherwise, we accumulate the requests via the helper method then check again if the `Subscription` arrived or not. If it arrived in the meantime, we atomically exchange the accumulated request value and if nonzero, request it from the newly retrieved `Subscription`. This non-blocking logic makes sure that in case of concurrent invocations of the two methods, no accumulated request is left behind. + +This complex logic and methods, along with other safeguards are available in the (internal) `SubscriptionHelper` utility class and can be used like this: + +```java +final class Operator<T> implements Subscriber<T>, Subscription { + final Subscriber<? super T> child; + + final AtomicReference<Subscription> ref = new AtomicReference<>(); + final AtomicLong requested = new AtomicLong(); + + public Operator(Subscriber<? super T> child) { + this.child = child; + } + + @Override + public void onSubscribe(Subscription s) { + SubscriptionHelper.deferredSetOnce(ref, requested, s); + } + + @Override + public void onNext(T t) { ... } + + @Override + public void onError(Throwable t) { ... } + + @Override + public void onComplete() { ... } + + @Override + public void cancel() { + SubscriptionHelper.cancel(ref); + } + + @Override + public void request(long n) { + SubscriptionHelper.deferredRequested(ref, requested, n); + } +} + +Operator<T> parent = new Operator<T>(child); + +child.onSubscribe(parent); + +source.subscribe(parent); +``` + +The second form is when multiple `Subscription`s replace each other and we not only need to hold onto request amounts when there is none of them but make sure a newer `Subscription` is requested only that much the previous `Subscription`'s upstream didn't deliver. This is called **Subscription arbitration** and the relevant algorithms are quite verbose and will be omitted here. There is, however, an utility class that manages this: (internal) `SubscriptionArbiter`. + +You can extend it (to save on object headers) or have it as a field. Its main use is to send it to the downstream via `onSubscribe` and update its current `Subscription` in the current operator. Note that even though its methods are thread-safe, it is intended for swapping `Subscription`s when the current one finished emitting events. This makes sure that any newer `Subscription` is requested the right amount and not more due to production/switch race. + +```java +final SubscriptionArbiter arbiter = ... + +// ... + +child.onSubscribe(arbiter); + +// ... + +long produced; + +@Override +public void onSubscribe(Subscription s) { + arbiter.setSubscription(s); +} + +@Override +public void onNext(T value) { + produced++; + child.onNext(value); +} + +@Override +public void onComplete() { + long p = produced; + if (p != 0L) { + arbiter.produced(p); + } + subscribeNext(); +} +``` + +For better performance, most operators can count the produced element amount and issue a single `SubscriptionArbiter.produced()` call just before switching to the next `Subscription`. + + +## Atomic error management + +In some cases, multiple sources may signal a `Throwable` at the same time but the contract forbids calling `onError` multiple times. Once can, of course use the **once** approach with `AtomicReference<Throwable>` and throw out but the first one to set the `Throwable` on it. + +The alternative is to collect these `Throwable`s into a `CompositeException` as long as possible and at one point lock out the others. This works by doing a copy-on-write scheme or by linking `CompositeException`s atomically and having a terminal sentinel to indicate all further errors should be dropped. + +```java +static final Throwable TERMINATED = new Throwable(); + +static boolean addThrowable(AtomicReference<Throwable> ref, Throwable e) { + for (;;) { + Throwable current = ref.get(); + if (current == TERMINATED) { + return false; + } + Throwable next; + if (current == null) { + next = e; + } else { + next = new CompositeException(current, e); + } + if (ref.compareAndSet(current, next)) { + return true; + } + } +} + +static Throwable terminate(AtomicReference<Throwable> ref) { + return ref.getAndSet(TERMINATED); +} +``` + +as with most common logic, this is supported by the (internal) `ExceptionHelper` utility class and the (internal) `AtomicThrowable` class. + +The usage pattern looks as follows: + +```java + +final AtomicThrowable errors = ...; + +@Override +public void onError(Throwable e) { + if (errors.addThrowable(e)) { + drain(); + } else { + RxJavaPlugins.onError(e); + } +} + +void drain() { + // ... + if (errors.get() != null) { + child.onError(errors.terminate()); + return; + } + // ... +} +``` + +## Half-serialization + +Sometimes having the queue-drain, `SerializedSubscriber` or `SerializedObserver` is a bit of an overkill. Such cases include when there is only one thread calling `onNext` but other threads may call `onError` or `onComplete` concurrently. Example operators include `takeUntil` where the other source may "interrupt" the main sequence and inject an `onComplete` into it before the main source itself would complete some time later. This is what I call **half-serialization**. + +The approach uses the concepts of the deferred actions and atomic error management discussed above and has 3 methods for the `onNext`, `onError` and `onComplete` management: + +```java +public static <T> void onNext(Subscriber<? super T> subscriber, T value, + AtomicInteger wip, AtomicThrowable error) { + if (wip.get() == 0 && wip.compareAndSet(0, 1)) { + subscriber.onNext(value); + if (wip.decrementAndGet() != 0) { + Throwable ex = error.terminate(); + if (ex != null) { + subscriber.onError(ex); + } else { + subscriber.onComplete(); + } + } + } +} + +public static void onError(Subscriber<?> subscriber, Throwable ex, + AtomicInteger wip, AtomicThrowable error) { + if (error.addThrowable(ex)) { + if (wip.getAndIncrement() == 0) { + subscriber.onError(error.terminate()); + } + } else { + RxJavaPlugins.onError(ex); + } +} + +public static void onComplete(Subscriber<?> subscriber, + AtomicInteger wip, AtomicThrowable error) { + if (wip.getAndIncrement() == 0) { + Throwable ex = error.terminate(); + if (ex != null) { + subscriber.onError(ex); + } else { + subscriber.onComplete(); + } + } +} +``` + +Here, the `wip` counter indicates there is an active emission happening and if found non-zero when trying to leave the `onNext`, it is taken as indication there was a concurrent `onError` or `onComplete()` call and the child must be notified. All subsequent calls to any of these methods are ignored. In this case, the `wip` is never decremented back to zero. + +RxJava 2.x, again, supports these with the (internal) utility class `HalfSerializer` and allows targeting `Subscriber`s and `Observer`s with it. + +## Fast-path queue-drain + +In some operators, it is unlikely concurrent threads try to enter into the drain loop at the same time and having to play the full enqueue-increment-dequeue adds unnecessary overhead. + +Luckily, such situations can be detected by a simple compare-and-set attempt on the work-in-progress counter, trying to change the amount from 0 to 1. If it fails, there is a concurrent drain in progress and we revert back to the classical queue-drain logic. If succeeds, we don't enqueue anything but emit the value / perform the action right there and we try to leave the serialized section. + +```java +public void onNext(T v) { + if (wip.get() == 0 && wip.compareAndSet(0, 1)) { + child.onNext(v); + if (wip.decrementAndGet() == 0) { + break; + } + } else { + queue.offer(v); + if (wip.getAndIncrement() != 0) { + break; + } + } + drainLoop(); +} + +void drain() { + if (getAndIncrement() == 0) { + drainLoop(); + } +} + +void drainLoop() { + // the usual drain loop part after the classical getAndIncrement() +} +``` + +In this pattern, the classical `drain` is spit into `drain` and `drainLoop`. The new `drain` does the increment-check and calls `drainLoop` and `drainLoop` contains the remaining logic with the loop, emission and wip management as usual. + +On the fast path, when we try to leave it, it is possible a concurrent call to `onNext` or `drain` incremented the `wip` counter further and the decrement didn't return it to zero. This is an indication for further work and we call `drainLoop` to process it. + +## FlowableSubscriber + +Version 2.0.7 introduced a new interface, `FlowableSubscriber` that extends `Subscriber` from Reactive-Streams. It has the same methods with the same parameter types but different textual rules attached to it, a set of relaxations to the Reactive-Streams specification to enable better performing RxJava internals while still honoring the specification to the letter for non-RxJava consumers of `Flowable`s. + +The rule relaxations are as follows: + +- §1.3 relaxation: `onSubscribe` may run concurrently with onNext in case the `FlowableSubscriber` calls `request()` from inside `onSubscribe` and it is the responsibility of `FlowableSubscriber` to ensure thread-safety between the remaining instructions in `onSubscribe` and `onNext`. +- §2.3 relaxation: calling `Subscription.cancel` and `Subscription.request` from `FlowableSubscriber.onComplete()` or `FlowableSubscriber.onError()` is considered a no-operation. +- §2.12 relaxation: if the same `FlowableSubscriber` instance is subscribed to multiple sources, it must ensure its `onXXX` methods remain thread safe. +- §3.9 relaxation: issuing a non-positive `request()` will not stop the current stream but signal an error via `RxJavaPlugins.onError`. + +When a `Flowable` gets subscribed by a `Subscriber`, an `instanceof` check will detect `FlowableSubscriber` and not apply the `StrictSubscriber` wrapper that makes sure the relaxations don't happen. In practice, ensuring rule §3.9 has the most overhead because a bad request may happen concurrently with an emission of any normal event and thus has to be serialized with one of the methods described in previous sections. + +**In fact, 2.x was always implemented in this relaxed manner thus looking at existing code and style is the way to go.** + +Therefore, it is strongly recommended one implements custom intermediate and end operators via `FlowableSubscriber`. + +From a source operator's perspective, extending the `Flowable` class and implementing `subscribeActual` has no need for +dispatching over the type of the `Subscriber`; the backing infrastructure already applies wrapping if necessary thus one can be sure in `subscribeActual(Subscriber<? super T> s)` the parameter `s` is a `FlowableSubscriber`. (The signature couldn't be changed for compatibility reasons.) Since the two interfaces on the Java level are the same, no real preferential treating is necessary within sources (i.e., don't cast `s` into `FlowableSubscriber`. + +The other base reactive consumers, `Observer`, `SingleObserver`, `MaybeObserver` and `CompletableObserver` don't need such relaxation, because + +- they are only defined and used in RxJava (i.e., no other library implemented with them), +- they were conceptionally always derived from the relaxed `Subscriber` RxJava had, +- they don't have backpressure thus no `request()` call that would introduce another concurrency to think about, +- there is no way to trigger emission from upstream before `onSubscribe(Disposable)` returns in standard operators (again, no `request()` method). + +# Backpressure and cancellation + +Backpressure (or flow control) in Reactive-Streams is the means to tell the upstream how many elements to produce or to tell it to stop producing elements altogether. Unlike the name suggest, there is no physical pressure preventing the upstream from calling `onNext` but the protocol to honor the request amount. + +## Replenishing + +When dealing with basic transformations in a flow, there are often cases when the number of items the upstream sends should be different what the downstream receives. Some operators may want to filter out certain items, others would batch up items before sending one item to the downstream. + +However, when an item is not forwarded by an operator, the downstream has no way of knowing its `request(1)` triggered an item generation that got dropped/buffered. Therefore, it can't know to (nor should it) repeat `request(1)` to "nudge" the source somewhere more upstream to try producing another item which now hopefully will result in an item being received by the downstream. Unlike, say the ACK-NACK based protocols, the requesting specified by the Reactive Streams are to be treated as cumulative. In the previous example, an impatient downstream would have 2 outstanding requests. + +Therefore, if an operator is not guaranteed to relay an upstream item to downstream, and thus keeping a 1:1 ratio, it has the duty to keep requesting items from the upstream until said operator ends up in a position to supply an item to the downstream. + +This may sound a bit complicated, but perhaps a demonstration of a `filter` operator can help: + +```java +final class FilterOddSubscriber implements FlowableSubscriber<Integer>, Subscription { + + final Subscriber<? super Integer> downstream; + + Subscription upstream; + + // ... + + @Override + public void onSubscribe(Subscription s) { + if (upstream != null) { + s.cancel(); + } else { + upstream = s; + downstream.onSubscribe(this); + } + } + + @Override + public void onNext(Integer item) { + if (item % 2 != 0) { + downstream.onNext(item); + } else { + upstream.request(1); + } + } + + @Override + public void request(long n) { + upstream.request(n); + } + + // the rest omitted for brevity +} +``` + +In such operators, thee downstream's `request` calls are forwarded to the upstream as is, and for `n` times (at most, unless completed) the `onNext` will be invoked. In this operator, we look for the odd numbers of the flow. If we find one, the downstream will be notified. If the incoming item is even, we won't forward it to the downstream. However, the downstream is still expecting at least 1 item, but since the upstream and downstream practically talk to each other directly, the upstream considers its duty to generate items fulfilled. This misalignment is then resolved by requesting 1 more item from the upstream for the previous ignored item. If more items arrive that get ignored, more will be requested as replenishment. + +Given that backpressure involves some overhead in the form of one or more atomic operations, requesting one by one could add a lot of overhead if the number of items filtered out is significantly more than those that passed through. If necessary, this situation can be either solved by [decoupling the upstream and downstream's request management](#stable-prefetching) or using an RxJava-specific type and protocol extension in the form of [ConditionalSubscriber](#conditionalsubscriber)s. + +## Stable prefetching + +In a previous section, we saw primitives to deal with request accounting and delayed `Subscriptions`, but often, operators have to react to request amount changes as well. This comes up when the operator has to decouple the downstream request amount from the amount it requests from upstream, such as `observeOn`. + +Such logic can get quite complicated in operators but one of the simplest manifestation can be the `rebatchRequest` operator that combines request management with serialization to ensure that upstream is requested with a predictable pattern no matter how the downstream requested (less, more or even unbounded): + +```java +final class RebatchRequests<T> extends AtomicInteger +implements FlowableSubscriber<T>, Subscription { + + final Subscriber<? super T> child; + + final AtomicLong requested; + + final SpscArrayQueue<T> queue; + + final int batchSize; + + final int limit; + + Subscription s; + + volatile boolean done; + Throwable error; + + volatile boolean cancelled; + + long emitted; + + public RebatchRequests(Subscriber<? super T> child, int batchSize) { + this.child = child; + this.batchSize = batchSize; + this.limit = batchSize - (batchSize >> 2); // 75% of batchSize + this.requested = new AtomicLong(); + this.queue = new SpscArrayQueue<T>(batchSize); + } + + @Override + public void onSubscribe(Subscription s) { + this.s = s; + child.onSubscribe(this); + s.request(batchSize); + } + + @Override + public void onNext(T t) { + queue.offer(t); + drain(); + } + + @Override + public void onError(Throwable t) { + error = t; + done = true; + drain(); + } + + @Override + public void onComplete() { + done = true; + drain(); + } + + @Override + public void request(long n) { + BackpressureHelper.add(requested, n); + drain(); + } + + @Override + public void cancel() { + cancelled = true; + s.cancel(); + } + + void drain() { + // see next code example + } +} +``` + +Here we extend `AtomicInteger` since the work-in-progress counting happens more often and is worth avoiding the extra indirection. The class extends `Subscription` and it hands itself to the `child` `Subscriber` to capture its `request()` (and `cancel()`) calls and route it to the main `drain` logic. Some operators need only this, some other operators (such as `observeOn` not only routes the downstream request but also does extra cancellations (cancels the asynchrony providing `Worker` as well) in its `cancel()` method. + +**Important**: when implementing operators for `Flowable` and `Observable` in RxJava 2.x, you are not allowed to pass along an upstream `Subscription` or `Disposable` to the child `Subscriber`/`Observer` when the operator logic itself doesn't require hooking the `request`/`cancel`/`dispose` calls. The reason for this is how operator-fusion is implemented on top of `Subscription` and `Disposable` passing through `onSubscribe` in RxJava 2.x (and in **Reactor 3**). See the next section about operator-fusion. There is no fusion in `Single`, `Completable` or `Maybe` (because there is no requesting or unbounded buffering with them) and their operators can pass the upstream `Disposable` along as is. + +Next comes the `drain` method whose pattern appears in many operators (with slight variations on how and what the emission does). + +```java +void drain() { + if (getAndIncrement() != 0) { + return; + } + + int missed = 1; + + for (;;) { + long r = requested.get(); + long e = 0L; + long f = emitted; + + while (e != r) { + if (cancelled) { + return; + } + boolean d = done; + + if (d) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + return; + } + } + + T v = queue.poll(); + boolean empty = v == null; + + if (d && empty) { + child.onComplete(); + return; + } + + if (empty) { + break; + } + + child.onNext(v); + + e++; + if (++f == limit) { + s.request(f); + f = 0L; + } + } + + if (e == r) { + if (cancelled) { + return; + } + + if (done) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + return; + } + if (queue.isEmpty()) { + child.onComplete(); + return; + } + } + } + + if (e != 0L) { + BackpressureHelper.produced(requested, e); + } + + emitted = f; + missed = addAndGet(-missed); + if (missed == 0) { + break; + } + } +} +``` + +This particular pattern is called the **stable-request queue-drain**. Another variation doesn't care about request amount stability towards upstream and simply requests the amount it delivered to the child: + +```java +void drain() { + if (getAndIncrement() != 0) { + return; + } + + int missed = 1; + + for (;;) { + long r = requested.get(); + long e = 0L; + + while (e != r) { + if (cancelled) { + return; + } + boolean d = done; + + if (d) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + return; + } + } + + T v = queue.poll(); + boolean empty = v == null; + + if (d && empty) { + child.onComplete(); + return; + } + + if (empty) { + break; + } + + child.onNext(v); + + e++; + } + + if (e == r) { + if (cancelled) { + return; + } + + if (done) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + return; + } + if (queue.isEmpty()) { + child.onComplete(); + return; + } + } + } + + if (e != 0L) { + BackpressureHelper.produced(requested, e); + s.request(e); + } + + missed = addAndGet(-missed); + if (missed == 0) { + break; + } + } +} +``` + +The third variation allows delaying a potential error until the upstream has terminated and all normal elements have been delivered to the child: + +```java +final boolean delayError; + +void drain() { + if (getAndIncrement() != 0) { + return; + } + + int missed = 1; + + for (;;) { + long r = requested.get(); + long e = 0L; + + while (e != r) { + if (cancelled) { + return; + } + boolean d = done; + + if (d && !delayError) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + return; + } + } + + T v = queue.poll(); + boolean empty = v == null; + + if (d && empty) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + } else { + child.onComplete(); + } + return; + } + + if (empty) { + break; + } + + child.onNext(v); + + e++; + } + + if (e == r) { + if (cancelled) { + return; + } + + if (done) { + if (delayError) { + if (queue.isEmpty()) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + } else { + child.onComplete(); + } + return; + } + } else { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + return; + } + if (queue.isEmpty()) { + child.onComplete(); + return; + } + } + } + } + + if (e != 0L) { + BackpressureHelper.produced(requested, e); + s.request(e); + } + + missed = addAndGet(-missed); + if (missed == 0) { + break; + } + } +} +``` + +If the downstream cancels the operator, the `queue` may still hold elements which may get referenced longer than expected if the operator chain itself is referenced in some way. On the user level, applying `onTerminateDetach` will forget all references going upstream and downstream and can help with this situation. On the operator level, RxJava 2.x usually calls `clear()` on the `queue` when the sequence is cancelled or ends before the queue is drained naturally. This requires some slight change to the drain loop: + +```java +final boolean delayError; + +@Override +public void cancel() { + cancelled = true; + s.cancel(); + if (getAndIncrement() == 0) { + queue.clear(); // <---------------------------- + } +} + +void drain() { + if (getAndIncrement() != 0) { + return; + } + + int missed = 1; + + for (;;) { + long r = requested.get(); + long e = 0L; + + while (e != r) { + if (cancelled) { + queue.clear(); // <---------------------------- + return; + } + boolean d = done; + + if (d && !delayError) { + Throwable ex = error; + if (ex != null) { + queue.clear(); // <---------------------------- + child.onError(ex); + return; + } + } + + T v = queue.poll(); + boolean empty = v == null; + + if (d && empty) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + } else { + child.onComplete(); + } + return; + } + + if (empty) { + break; + } + + child.onNext(v); + + e++; + } + + if (e == r) { + if (cancelled) { + queue.clear(); // <---------------------------- + return; + } + + if (done) { + if (delayError) { + if (queue.isEmpty()) { + Throwable ex = error; + if (ex != null) { + child.onError(ex); + } else { + child.onComplete(); + } + return; + } + } else { + Throwable ex = error; + if (ex != null) { + queue.clear(); // <---------------------------- + child.onError(ex); + return; + } + if (queue.isEmpty()) { + child.onComplete(); + return; + } + } + } + } + + if (e != 0L) { + BackpressureHelper.produced(requested, e); + s.request(e); + } + + missed = addAndGet(-missed); + if (missed == 0) { + break; + } + } +} +``` + +Since the queue is single-producer-single-consumer, its `clear()` must be called from a single thread - which is provided by the serialization loop and is enabled by the `getAndIncrement() == 0` "half-loop" inside `cancel()`. + +An important note on the order of calls to `done` and the `queue`'s state: + +```java +boolean d = done; +T v = queue.poll(); +``` + +and + +```java +boolean d = done; +boolean empty = queue.isEmpty(); +``` + +These must happen in the order specified. If they were swapped, it is possible when the drain runs asynchronously to an `onNext`/`onComplete()`, the queue may appear empty at first, then it gets elements followed by `done = true` and a late `done` check in the drain loop may complete the sequence thinking it delivered all values there was. + +## Single valued results + +Sometimes an operator only emits one single value at some point instead of emitting more or all of its sources. Such operators include `fromCallable`, `reduce`, `any`, `all`, `first`, etc. + +The classical queue-drain works here but is a bit of an overkill to allocate objects to store the work-in-progress counter, request accounting and the queue itself. These elements can be reduced to a single state-machine with one state counter object - often inlinded by extending AtomicInteger - and a plain field for storing the single value to be emitted. + +The state machine handing the possible concurrent downstream requests and normal completion path is a bit complicated to show here and is quite easy to get wrong. + +RxJava 2.x supports this kind of behavior through the (internal) `DeferredScalarSubscription` for operators without an upstream source (`fromCallable`) and the (internal) `DeferredScalarSubscriber` for reduce-like operators with an upstream source. + +Using the `DeferredScalarSubscription` is straightforward, one creates it, sends it to the downstream via `onSubscribe` and later on calls `complete(T)` to signal the end with a single value: + +```java +DeferredScalarSubscription<Integer> dss = new DeferredScalarSubscription<>(child); +child.onSubscribe(dss); + +dss.complete(1); +``` + +Using the `DeferredScalarSubscriber` requires more coding and extending the class itself: + +```java +final class Counter extends DeferredScalarSubscriber<Object, Integer> { + public Counter(Subscriber<? super Integer> child) { + super(child); + value = 0; + hasValue = true; + } + + @Override + public void onNext(Object t) { + value++; + } +} +``` + +By default, the `DeferredScalarSubscriber.onSubscribe()` requests `Long.MAX_VALUE` from the upstream (but the method can be overridden in subclasses). + +## Single-element post-complete + +Some operators have to modulate a sequence of elements in a 1:1 fashion but when the upstream terminates, they need to produce a final element followed by a terminal event (usually `onComplete`). + +```java +final class OnCompleteEndWith implements Subscriber<T>, Subscription { + final Subscriber<? super T> child; + + final T finalElement; + + Subscription s; + + public OnCompleteEndWith(Subscriber<? super T> child, T finalElement) { + this.child = child; + this.finalElement = finalElement; + } + + @Override + public void onSubscribe(Subscription s) { + this.s = s; + child.onSubscribe(this); + } + + @Override + public void onNext(T t) { + child.onNext(t); + } + + @Overide + public void onError(Throwable t) { + child.onError(t); + } + + @Override + public void onComplete() { + child.onNext(finalElement); + child.onComplete(); + } + + @Override + public void request(long n) { + s.request(n); + } + + @Override + public void cancel() { + s.cancel(); + } +} +``` + +This works if the downstream request more than the upstream produces + 1, otherwise the call to `onComplete` may overflow the child `Subscriber`. + +Heavyweight solutions such as queue-drain or `SubscriptionArbiter` with `ScalarSubscriber` can be used here, however, there is a more elegant solution to the problem. + +The idea is that request amounts occupy only 63 bits of a 64 bit (atomic) long type. If we'd mask out the lower 63 bits when working with the amount, we can use the most significant bit to indicate the upstream sequence has finished and then on, any 0 to n request amount change can trigger the emission of the `finalElement`. Since a downstream `request()` can race with an upstream `onComplete`, marking the bit atomically via a compare-and-set ensures correct state transition. + +For this, the `OnCompleteEndWith` has to be changed by adding an `AtomicLong` for accounting requests, a long for counting the production, then updating `request()` and `onComplete()` methods: + +```java + +final class OnCompleteEndWith +extends AtomicLong +implements FlowableSubscriber<T>, Subscription { + final Subscriber<? super T> child; + + final T finalElement; + + Subscription s; + + long produced; + + static final class long REQUEST_MASK = Long.MAX_VALUE; // 0b01111...111L + static final class long COMPLETE_MASK = Long.MIN_VALUE; // 0b10000...000L + + public OnCompleteEndWith(Subscriber<? super T> child, T finalElement) { + this.child = child; + this.finalElement = finalElement; + } + + @Override + public void onSubscribe(Subscription s) { ... } + + @Override + public void onNext(T t) { + produced++; // <------------------------ + child.onNext(t); + } + + @Overide + public void onError(Throwable t) { ... } + + @Override + public void onComplete() { + long p = produced; + if (p != 0L) { + produced = 0L; + BackpressureHelper.produced(this, p); + } + + for (;;) { + long current = get(); + if ((current & COMPLETE_MASK) != 0) { + break; + } + if ((current & REQUEST_MASK) != 0) { + lazySet(Long.MIN_VALUE + 1); + child.onNext(finalElement); + child.onComplete(); + return; + } + if (compareAndSet(current, COMPLETE_MASK)) { + break; + } + } + } + + @Override + public void request(long n) { + for (;;) { + long current = get(); + if ((current & COMPLETE_MASK) != 0) { + if (compareAndSet(current, COMPLETE_MASK + 1)) { + child.onNext(finalElement); + child.onComplete(); + } + break; + } + long u = BackpressureHelper.addCap(current, n); + if (compareAndSet(current, u)) { + s.request(n); + break; + } + } + } + + @Override + public void cancel() { ... } +} +``` + +RxJava 2 has a couple of operators, `materialize`, `mapNotification`, `onErrorReturn`, that require this type of behavior and for that, the (internal) `SinglePostCompleteSubscriber` class captures the algorithms above: + +```java +final class OnCompleteEndWith<T> extends SinglePostCompleteSubscriber<T, T> { + final Subscriber<? super T> child; + + public OnCompleteEndWith(Subscriber<? super T> child, T finalElement) { + this.child = child; + this.value = finalElement; + } + + @Override + public void onNext(T t) { + produced++; // <------------------------ + child.onNext(t); + } + + @Overide + public void onError(Throwable t) { ... } + + @Override + public void onComplete() { + complete(value); + } +} +``` + +## Multi-element post-complete + +Certain operators may need to emit multiple elements after the main sequence completes, which may or may not relay elements from the live upstream before its termination. An example operator is `buffer(int, int)` when the skip < size yielding overlapping buffers. In this operator, it is possible when the upstream completes, several overlapping buffers are waiting to be emitted to the child but that has to happen only when the child actually requested more buffers. + +The state machine for this case is complicated but RxJava has two (internal) utility methods on `QueueDrainHelper` for dealing with the situation: + +```java +<T> void postComplete(Subscriber<? super T> actual, + Queue<T> queue, + AtomicLong state, + BooleanSupplier isCancelled); + +<T> boolean postCompleteRequest(long n, + Subscriber<? super T> actual, + Queue<T> queue, + AtomicLong state, + BooleanSupplier isCancelled); +``` + +They take the child `Subscriber`, the queue to drain from, the state holding the current request amount and a callback to see if the downstream cancelled the sequence. + +Usage of these methods is as follows: + +```java +final class EmitTwice<T> extends AtomicLong +implements FlowableSubscriber<T>, Subscription, BooleanSupplier { + final Subscriber<? super T> child; + + final ArrayDeque<T> buffer; + + volatile boolean cancelled; + + Subscription s; + + long produced; + + public EmitTwice(Subscriber<? super T> child) { + this.child = child; + this.buffer = new ArrayDeque<>(); + } + + @Override + public void onSubscribe(Subscription s) { + this.s = s; + child.onSubscribe(this); + } + + @Override + public void onNext(T t) { + produced++; + buffer.offer(t); + child.onNext(t); + } + + @Override + public void onError(Throwable t) { + buffer.clear(); + child.onError(t); + } + + @Override + public void onComplete() { + long p = produced; + if (p != 0L) { + produced = 0L; + BackpressureHelper.produced(this, p); + } + QueueDrainHelper.postComplete(child, buffer, this, this); + } + + @Override + public boolean getAsBoolean() { + return cancelled; + } + + @Override + public void cancel() { + cancelled = true; + s.cancel(); + } + + @Override + public void request(long n) { + if (!QueueDrainHelper.postCompleteRequest(n, child, buffer, this, this)) { + s.request(n); + } + } +} +``` + +# Creating operator classes + +Creating operator implementations in 2.x is simpler than in 1.x and incurs less allocation as well. You have the choice to implement your operator as a `Subscriber`-transformer to be used via `lift` or as a fully-fledged base reactive class. + +## Operator by extending a base reactive class + +In 1.x, extending `Observable` was possible but convoluted because you had to implement the `OnSubscribe` interface separately and pass it to `Observable.create()` or to the `Observable(OnSubscribe)` protected constructor. + +In 2.x, all base reactive classes are abstract and you can extend them directly without any additional indirection: + +```java +public final class FlowableMyOperator extends Flowable<Integer> { + final Publisher<Integer> source; + + public FlowableMyOperator(Publisher<Integer> source) { + this.source = source; + } + + @Override + protected void subscribeActual(Subscriber<? super Integer> s) { + source.map(v -> v + 1).subscribe(s); + } +} +``` + +When taking other reactive types as inputs in these operators, it is recommended one defines the base reactive interfaces instead of the abstract classes, allowing better interoperability between libraries (especially with `Flowable` operators and other Reactive-Streams `Publisher`s). To recap, these are the class-interface pairs: + + - `Flowable` - `Publisher` - `FlowableSubscriber`/`Subscriber` + - `Observable` - `ObservableSource` - `Observer` + - `Single` - `SingleSource` - `SingleObserver` + - `Completable` - `CompletableSource` - `CompletableObserver` + - `Maybe` - `MaybeSource` - `MaybeObserver` + +RxJava 2.x locks down `Flowable.subscribe` (and the same methods in the other types) in order to provide runtime hooks into the various flows, therefore, implementors are given the `subscribeActual()` to be overridden. When it is invoked, all relevant hooks and wrappers have been applied. Implementors should avoid throwing unchecked exceptions as the library generally can't deliver it to the respective `Subscriber` due to lifecycle restrictions of the Reactive-Streams specification and sends it to the global error consumer via `RxJavaPlugins.onError`. + +Unlike in 1.x, In the example above, the incoming `Subscriber` is simply used directly for subscribing again (but still at most once) without any kind of wrapping. In 1.x, one needs to call `Subscribers.wrap` to avoid double calls to `onStart` and cause unexpected double initialization or double-requesting. + +Unless one contributes a new operator to RxJava, working with such classes may become tedious, especially if they are intermediate operators: + +```java +new FlowableThenSome( + new FlowableOther( + new FlowableMyOperator(Flowable.range(1, 10).map(v -> v * v)) + ) +) +``` + +This is an unfortunate effect of Java lacking extension method support. A possible ease on this burden is by using `compose` to have fluent inline application of the custom operator: + +```java +Flowable.range(1, 10).map(v -> v * v) +.compose(f -> new FlowableOperatorWithParameter(f, 10)); + +Flowable.range(1, 10).map(v -> v * v) +.compose(FlowableMyOperator::new); +``` + +## Operator targeting lift() + +The alternative to the fluent application problem is to have a `Subscription`-transformer implemented instead of extending the whole reactive base class and use the respective type's `lift()` operator to get it into the sequence. + +First one has to implement the respective `XOperator` interface: + +```java +public final class MyOperator implements FlowableOperator<Integer, Integer> { + + @Override + public Subscriber<? super Integer> apply(Subscriber<? super Integer> child) { + return new Op(child); + } + + static final class Op implements FlowableSubscriber<Integer>, Subscription { + final Subscriber<? super Integer> child; + + Subscription s; + + public Op(Subscriber<? super Integer> child) { + this.child = child; + } + + @Override + pubic void onSubscribe(Subscription s) { + this.s = s; + child.onSubscribe(this); + } + + @Override + public void onNext(Integer v) { + child.onNext(v * v); + } + + @Override + public void onError(Throwable e) { + child.onError(e); + } + + @Override + public void onComplete() { + child.onComplete(); + } + + @Override + public void cancel() { + s.cancel(); + } + + @Override + public void request(long n) { + s.request(n); + } + } +} +``` + +You may recognize that implementing operators via extension or lifting looks quite similar. In both cases, one usually implements a `FlowableSubscriber` (`Observer`, etc) that takes a downstream `Subscriber`, implements the business logic in the `onXXX` methods and somehow (manually or as part of `lift()`'s lifecycle) gets subscribed to an upstream source. + +The benefit of applying the Reactive-Streams design to all base reactive types is that each consumer type is now an interface and can be applied to operators that have to extend some class. This was a pain in 1.x because `Subscriber` and `SingleSubscriber` are classes themselves, plus `Subscriber.request()` is a protected-final method and an operator's `Subscriber` can't implement the `Producer` interface at the same time. In 2.x there is no such problem and one can have both `Subscriber`, `Subscription` or even `Observer` together in the same consumer type. + +# Operator fusion + +Operator fusion has the premise that certain operators can be combined into one single operator (macro-fusion) or their internal data structures shared between each other (micro-fusion) that allows fewer allocations, lower overhead and better performance. + +This advanced concept was invented, worked out and studied in the [Reactive-Streams-Commons](https://github.com/reactor/reactive-streams-commons) research project manned by the leads of RxJava and Project Reactor. Both libraries use the results in their implementation, which look the same but are incompatible due to different classes and packages involved. In addition, RxJava 2.x's approach is a more polished version of the invention due to delays between the two project's development. + +Since operator-fusion is optional, you may chose to not bother making your operator fusion-enabled. The `DeferredScalarSubscription` is fusion-enabled and needs no additional development in this regard though. + +If you chose to ignore operator-fusion, you still have to follow the requirement of never forwarding a `Subscription`/`Disposable` coming through `onSubscribe` of `Subscriber`/`Observer` as this may break the fusion protocol and may skip your operator's business logic entirely: + +```java +final class SomeOp<T> implements Subscriber<T>, Subscription { + + // ... + Subscription s; + + public void onSubscribe(Subscription s) { + this.s = s; + child.onSubscribe(this); // <--------------------------- + } + + @Override + public void cancel() { + s.cancel(); + } + + @Override + public void request(long n) { + s.request(n); + } + + // ... +} +``` + +Yes, this adds one more indirection between operators but it is still cheap (and would be necessary for the operator anyway) but enables huge performance gains with the right chain of operators. + +## Generations + +Given this novel approach, a generation number can be assigned to various implementation styles of reactive architectures: + +#### Generation 0 +These are the classical libraries that either use `java.util.Observable` or are listener based (Java Swing's `ActionListener`). Their common property is that they don't support composition (of events and cancellation). See also **Google Agera**. + +#### Generation 1 +This is the level of the **Rx.NET** library (even up to 3.x) that supports composition, but has no notion for backpressure and doesn't properly support synchronous cancellation. Many JavaScript libraries such as **RxJS 5** are still on this level. See also **Google gRPC**. + +#### Generation 2 +This is what **RxJava 1.x** is categorized, it supports composition, backpressure and synchronous cancellation along with the ability to lift an operator into a sequence. + +#### Generation 3 +This is the level of the Reactive-Streams based libraries such as **Reactor 2** and **Akka-Stream**. They are based upon a specification that evolved out of RxJava but left behind its drawbacks (such as the need to return anything from `subscribe()`). This is incompatible with RxJava 1.x and thus 2.x had to be rewritten from scratch. + +#### Generation 4 +This level expands upon the Reactive-Streams interfaces with operator-fusion (in a compatible fashion, that is, op-fusion is optional between two stages and works without them). **Reactor 3** and **RxJava 2** are at this level. The material around **Akka-Stream** mentions operator-fusion as well, however, **Akka-Stream** is not a native Reactive-Streams implementation (requires a materializer to get a `Publisher` out) and as such it is only Gen 3. + +There are discussions among the 4th generation library providers to have the elements of operator-fusion standardized in Reactive-Streams 2.0 specification (or in a neighboring extension) and have **RxJava 3** and **Reactor 4** work together on that aspect as well. + +## Components + +### Callable and ScalarCallable + +Certain `Flowable` sources, similar to `Single` or `Completable` are known to ever emit zero or one item and that single item is known to be constant or is computed synchronously. Well known examples of this are `just()`, `empty()` and `fromCallable`. Subscribing to these sources, like any other sources, adds the same infrastructure overhead which can often be avoided if the consumer could just pick or have the item calculated on the spot. + +For example, `just` and `empty` appears as the mapping result of a `flatMap` operation: + +```java +source.flatMap(v -> { + if (v % 2 == 0) { + return just(v); + } + return empty(); +}) +``` + +Here, if we'd somehow recognize that `empty()` won't emit a value but only `onComplete` we could simply avoid subscribing to it inside `flatMap`, saving on the overhead. Similarly, recognizing that `just` emits exactly one item we can route it differently inside `flatMap` and again, avoiding creating a lot of objects to get to the same single item. + +In other times, knowing the emission property can simplify or chose a different operator instead of the applied one. For example, applying `flatMap` to an `empty()` source has no use since there won't be any item to be flattened into a sequence; the whole flattened sequence is going to be empty. Knowing that a source is `just` to `flatMap`, there is no need for the complicated inner mechanisms as there is going to be only one mapped inner source and one can subscribe the downstream's `Subscriber` to it directly. + +```java +Flowable.just(1).flatMap(v -> Flowable.range(v, 5)).subscribe(...); + +// in some specialized operator: + +T value; // from just() + +@Override +public void subscribeActual(Subscriber<? super T> s) { + mapper.apply(value).subscribe(s); +} +``` + +There could be other sources with these properties, therefore, RxJava 2 uses the `io.reactivex.internal.fusion.ScalarCallable` and `java.util.Callable` interfaces to indicate a source is a constant or sequentially computable. When a source `Flowable` or `Observable` is marked with one of these interfaces, many fusion enabled operators will perform special actions to avoid the overhead of a normal and general source. + +We use Java's own and preexisting `java.util.Callable` interface to indicate a synchronously computable source. The `ScalarCallable` is an extension to this interface by which it suppresses the `throws Exception` of `Callable.call()`: + +```java +interface Callable<T> { + T call() throws Exception; +} + +interface ScalarCallable<T> extends Callable<T> { + @Override + T call(); +} +``` + +The reason for the two separate interfaces is that if a source is constant, like `just`, one can perform assembly-time optimizations with it knowing that each regular `subscribe` invocation would have resulted in the same single value. + +`Callable` denotes sources, such as `fromCallable` that indicates the single value has to be calculated at runtime of the flow. By this logic, you can see that `ScalarCallable` is a `Callable` on its own right because the constant can be "calculated" as late as the runtime phase of the flow. + +Since Reactive-Streams forbids using `null`s as emission values, we can use `null` in `(Scalar)Callable` marked sources to indicate there is no value to be emitted, thus one can't mistake an user's `null` with the empty indicator `null`. For example, this is how `empty()` is implemented: + +```java +final class FlowableEmpty extends Flowable<Object> implements ScalarCallable<Object> { + @Override + public void subscribeActual(Subscriber<? super T> s) { + EmptySubscription.complete(s); + } + + @Override + public Object call() { + return null; // interpreted as no value available + } +} +``` + +Sources implementing `Callable` may throw checked exceptions from `call()` which is handled by the consumer operators as an indication to signal `onError` in an operator specific manner (such as delayed). + +```java +final class FlowableIOException extends Flowable<Object> implements Callable<Object> { + @Override + public void subscribeActual(Subscriber<? super T> s) { + EmptySubscription.error(new IOException(), s); + } + + @Override + public Object call() throws Exception { + throw new IOException(); + } +} +``` + +However, implementors of `ScalarCallable` should avoid throwing any exception and limit the code in `call()` be constant or simple computation that can be legally executed during assembly time. + +As the consumer of sources, one may want to deal with such kind of special `Flowable`s or `Observable`s. For example, if you create an operator that can leverage the knowledge of a single element source as its main input, you can check the types and extract the value of a `ScalarCallable` at assembly time right in the operator: + +```java +// Flowable.java +public final Flowable<Integer> plusOne() { + if (this instanceof ScalarCallable) { + Integer value = ((ScalarCallable<Integer>)this).call(); + if (value == null) { + return empty(); + } + return just(value + 1); + } + return cast(Integer.class).map(v -> v + 1); +} +``` + +or as a `FlowableTransformer`: + +```java +FlowableTransformer<Integer, Integer> plusOneTransformer = source -> { + if (source instanceof ScalarCallable) { + Integer value = ((ScalarCallable<Integer>)source).call(); + if (value == null) { + return empty(); + } + return just(value + 1); + } + return source.map(v -> v + 1); +}; +``` + +However, it is not mandatory to handle `ScalarCallable`s and `Callable`s separately. Since the former extends the latter, the type check can be deferred till subscription time and handled with the same code path: + +```java +final class FlowablePlusOne extends Flowable<Integer> { + final Publisher<Integer> source; + + FlowablePlusOne(Publisher<Integer> source) { + this.source = source; + } + + @Override + public void subscribeActual(Subscriber<? super Integer> s) { + if (source instanceof Callable) { + Integer value; + + try { + value = ((Callable<Integer>)source).call(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptySubscription.error(ex, s); + return; + } + + s.onSubscribe(new ScalarSubscription<Integer>(s, value + 1)); + } else { + new FlowableMap<>(source, v -> v + 1).subscribe(s); + } + } +} +``` + +### ConditionalSubscriber + +TBD + +### QueueSubscription and QueueDisposable + +TBD + +# Example implementations + +TBD + +## `map` + `filter` hybrid + +TBD + +## Ordered `merge` + +TBD diff --git a/docs/_Footer.md b/docs/_Footer.md new file mode 100644 index 0000000000..8ecc48ce6f --- /dev/null +++ b/docs/_Footer.md @@ -0,0 +1,2 @@ +**Copyright (c) 2016-present, RxJava Contributors.** +[Twitter @RxJava](https://twitter.com/#!/RxJava) | [Gitter @RxJava](https://gitter.im/ReactiveX/RxJava) diff --git a/docs/_Sidebar.md b/docs/_Sidebar.md new file mode 100644 index 0000000000..22961acec3 --- /dev/null +++ b/docs/_Sidebar.md @@ -0,0 +1,32 @@ +* [Introduction](https://github.com/ReactiveX/RxJava/wiki/Home) +* [Getting Started](https://github.com/ReactiveX/RxJava/wiki/Getting-Started) +* [How to Use RxJava](https://github.com/ReactiveX/RxJava/wiki/How-To-Use-RxJava) +* [Reactive Streams](https://github.com/ReactiveX/RxJava/wiki/Reactive-Streams) +* [The reactive types of RxJava](https://github.com/ReactiveX/RxJava/wiki/Observable) +* [Schedulers](https://github.com/ReactiveX/RxJava/wiki/Scheduler) +* [Subjects](https://github.com/ReactiveX/RxJava/wiki/Subject) +* [Error Handling](https://github.com/ReactiveX/RxJava/wiki/Error-Handling) +* [Operators (Alphabetical List)](https://github.com/ReactiveX/RxJava/wiki/Alphabetical-List-of-Observable-Operators) + * [Async](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) + * [Blocking](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) + * [Combining](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables) + * [Conditional & Boolean](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators) + * [Connectable](https://github.com/ReactiveX/RxJava/wiki/Connectable-Observable-Operators) + * [Creation](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables) + * [Error management](https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators) + * [Filtering](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) + * [Mathematical and Aggregate](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators) + * [Parallel flows](https://github.com/ReactiveX/RxJava/wiki/Parallel-flows) + * [String](https://github.com/ReactiveX/RxJava/wiki/String-Observables) + * [Transformation](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables) + * [Utility](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) + * [Notable 3rd party Operators (Alphabetical List)](https://github.com/ReactiveX/RxJava/wiki/Alphabetical-List-of-3rd-party-Operators) + * [Operator matrix](https://github.com/ReactiveX/RxJava/wiki/Operator-Matrix) +* [Plugins](https://github.com/ReactiveX/RxJava/wiki/Plugins) +* [How to Contribute](https://github.com/ReactiveX/RxJava/wiki/How-to-Contribute) +* [Writing operators](https://github.com/ReactiveX/RxJava/wiki/Writing-operators-for-2.0) +* [Backpressure](https://github.com/ReactiveX/RxJava/wiki/Backpressure-(2.0)) + * [another explanation](https://github.com/ReactiveX/RxJava/wiki/Backpressure) +* JavaDoc: [1.x](http://reactivex.io/RxJava/1.x/javadoc), [2.x](http://reactivex.io/RxJava/2.x/javadoc), [3.x](http://reactivex.io/RxJava/3.x/javadoc) +* [Coming from RxJava 1](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0) +* [Additional Reading](https://github.com/ReactiveX/RxJava/wiki/Additional-Reading) diff --git a/docs/_Sidebar.md.md b/docs/_Sidebar.md.md new file mode 100644 index 0000000000..32e201fc46 --- /dev/null +++ b/docs/_Sidebar.md.md @@ -0,0 +1,32 @@ +* [Introduction](https://github.com/ReactiveX/RxJava/wiki/Home) +* [Getting Started](https://github.com/ReactiveX/RxJava/wiki/Getting-Started) +* [How to Use RxJava](https://github.com/ReactiveX/RxJava/wiki/How-To-Use-RxJava) +* [Reactive Streams](https://github.com/ReactiveX/RxJava/wiki/Reactive-Streams) +* [The reactive types of RxJava](https://github.com/ReactiveX/RxJava/wiki/Observable) +* [Schedulers](https://github.com/ReactiveX/RxJava/wiki/Scheduler) +* [Subjects](https://github.com/ReactiveX/RxJava/wiki/Subject) +* [Error Handling](https://github.com/ReactiveX/RxJava/wiki/Error-Handling) +* [Operators (Alphabetical List)](https://github.com/ReactiveX/RxJava/wiki/Alphabetical-List-of-Observable-Operators) + * [Async](https://github.com/ReactiveX/RxJava/wiki/Async-Operators) + * [Blocking](https://github.com/ReactiveX/RxJava/wiki/Blocking-Observable-Operators) + * [Combining](https://github.com/ReactiveX/RxJava/wiki/Combining-Observables) + * [Conditional & Boolean](https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators) + * [Connectable](https://github.com/ReactiveX/RxJava/wiki/Connectable-Observable-Operators) + * [Creation](https://github.com/ReactiveX/RxJava/wiki/Creating-Observables) + * [Error management](https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators) + * [Filtering](https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables) + * [Mathematical and Aggregate](https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators) + * [Parallel flows](https://github.com/ReactiveX/RxJava/wiki/Parallel-flows) + * [String](https://github.com/ReactiveX/RxJava/wiki/String-Observables) + * [Transformation](https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables) + * [Utility](https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators) + * [Notable 3rd party Operators (Alphabetical List)](https://github.com/ReactiveX/RxJava/wiki/Alphabetical-List-of-3rd-party-Operators) + * [Operator matrix](https://github.com/ReactiveX/RxJava/wiki/Operator-Matrix) +* [Plugins](https://github.com/ReactiveX/RxJava/wiki/Plugins) +* [How to Contribute](https://github.com/ReactiveX/RxJava/wiki/How-to-Contribute) +* [Writing operators](https://github.com/ReactiveX/RxJava/wiki/Writing-operators-for-2.0) +* [Backpressure](https://github.com/ReactiveX/RxJava/wiki/Backpressure-(2.0)) + * [another explanation](https://github.com/ReactiveX/RxJava/wiki/Backpressure) +* [JavaDoc](http://reactivex.io/RxJava/2.x/javadoc) +* [Coming from RxJava 1](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0) +* [Additional Reading](https://github.com/ReactiveX/RxJava/wiki/Additional-Reading) diff --git a/gradle.properties b/gradle.properties index b5fd8018af..e685b8103a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,24 @@ -version=0.20.0-RC1-SNAPSHOT +group=io.reactivex.rxjava3 +version=3.0.0-SNAPSHOT +description=RxJava: Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM. + +POM_ARTIFACT_ID=rxjava +POM_NAME=RxJava +POM_PACKAGING=jar + +POM_DESCRIPTION=Reactive Extensions for Java +POM_INCEPTION_YEAR=2013 + +POM_URL=https://github.com/ReactiveX/RxJava +POM_SCM_URL=https://github.com/ReactiveX/RxJava +POM_SCM_CONNECTION=scm:git:git://github.com/ReactiveX/RxJava.git +POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/ReactiveX/RxJava.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=akarnokd +POM_DEVELOPER_NAME=David Karnok +POM_DEVELOPER_URL=https://github.com/akarnokd/ +POM_DEVELOPER_EMAIL=akarnokd@gmail.com diff --git a/gradle/buildscript.gradle b/gradle/buildscript.gradle deleted file mode 100644 index 2d8643103e..0000000000 --- a/gradle/buildscript.gradle +++ /dev/null @@ -1,17 +0,0 @@ -// Executed in context of buildscript -repositories { - // Repo in addition to maven central - repositories { maven { url 'http://dl.bintray.com/content/netflixoss/external-gradle-plugins/' } } // For gradle-release - maven { - //FIXME: waiting for https://github.com/johnrengelman/shadow/pull/38 to merge - name 'Shadow' - url 'http://dl.bintray.com/content/gvsmirnov/gradle-plugins' - } -} -dependencies { - classpath 'nl.javadude.gradle.plugins:license-gradle-plugin:0.6.1' - classpath 'com.mapvine:gradle-cobertura-plugin:0.1' - classpath 'gradle-release:gradle-release:1.1.5' - classpath 'org.ajoberstar:gradle-git:0.5.0' - classpath 'com.github.jengelman.gradle.plugins:shadow:0.8.1' -} diff --git a/gradle/check.gradle b/gradle/check.gradle deleted file mode 100644 index a3e4b4e7f5..0000000000 --- a/gradle/check.gradle +++ /dev/null @@ -1,26 +0,0 @@ -subprojects { -// Checkstyle -apply plugin: 'checkstyle' -checkstyle { - ignoreFailures = true - configFile = rootProject.file('codequality/checkstyle.xml') -} - -// FindBugs -apply plugin: 'findbugs' -findbugs { - ignoreFailures = true -} - -// PMD -apply plugin: 'pmd' -//tasks.withType(Pmd) { reports.html.enabled true } - -apply plugin: 'cobertura' -cobertura { - sourceDirs = sourceSets.main.java.srcDirs - format = 'html' - includes = ['**/*.java', '**/*.groovy'] - excludes = [] -} -} diff --git a/gradle/convention.gradle b/gradle/convention.gradle deleted file mode 100644 index f581db7cc5..0000000000 --- a/gradle/convention.gradle +++ /dev/null @@ -1,101 +0,0 @@ -// GRADLE-2087 workaround, perform after java plugin -status = project.hasProperty('preferredStatus')?project.preferredStatus:(version.contains('SNAPSHOT')?'snapshot':'release') - -subprojects { project -> - apply plugin: 'java' // Plugin as major conventions - - sourceCompatibility = 1.6 - - // Restore status after Java plugin - status = rootProject.status - - task sourcesJar(type: Jar, dependsOn:classes) { - from sourceSets.main.allSource - classifier 'sources' - extension 'jar' - } - - task javadocJar(type: Jar, dependsOn:javadoc) { - from javadoc.destinationDir - classifier 'javadoc' - extension 'jar' - } - - configurations.create('sources') - configurations.create('javadoc') - configurations.archives { - extendsFrom configurations.sources - extendsFrom configurations.javadoc - } - - // When outputing to an Ivy repo, we want to use the proper type field - gradle.taskGraph.whenReady { - def isNotMaven = !it.hasTask(project.uploadMavenCentral) - if (isNotMaven) { - def artifacts = project.configurations.sources.artifacts - def sourceArtifact = artifacts.iterator().next() - sourceArtifact.type = 'sources' - } - } - - artifacts { - sources(sourcesJar) { - // Weird Gradle quirk where type will be used for the extension, but only for sources - type 'jar' - } - javadoc(javadocJar) { - type 'javadoc' - } - } - - configurations { - provided { - description = 'much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive.' - transitive = true - visible = true - } - } - - project.sourceSets { - main.compileClasspath += project.configurations.provided - main.runtimeClasspath -= project.configurations.provided - test.compileClasspath += project.configurations.provided - test.runtimeClasspath += project.configurations.provided - } -} - -apply plugin: 'github-pages' // Used to create publishGhPages task - -def docTasks = [:] -[Javadoc,ScalaDoc,Groovydoc].each{ Class docClass -> - def allSources = allprojects.tasks*.withType(docClass).flatten()*.source - if (allSources) { - def shortName = docClass.simpleName.toLowerCase() - def docTask = task "aggregate${shortName.capitalize()}"(type: docClass, description: "Aggregate subproject ${shortName}s") { - source = allSources - destinationDir = file("${project.buildDir}/docs/${shortName}") - doFirst { - def classpaths = allprojects.findAll { it.plugins.hasPlugin(JavaPlugin) }.collect { it.sourceSets.main.compileClasspath } - classpath = files(classpaths) - } - } - docTasks[shortName] = docTask - processGhPages.dependsOn(docTask) - } -} - -githubPages { - repoUri = "git@github.com:Netflix/${rootProject.githubProjectName}.git" - pages { - docTasks.each { shortName, docTask -> - from(docTask.outputs.files) { - into "docs/${shortName}" - } - } - } -} - -// Generate wrapper, which is distributed as part of source to alleviate the need of installing gradle -task createWrapper(type: Wrapper) { - gradleVersion = '1.6' -} diff --git a/gradle/doclet-exclude.jar b/gradle/doclet-exclude.jar deleted file mode 100644 index 4e4fd96380..0000000000 Binary files a/gradle/doclet-exclude.jar and /dev/null differ diff --git a/gradle/javadocStyleSheet.css b/gradle/javadocStyleSheet.css deleted file mode 100644 index fdaada472e..0000000000 --- a/gradle/javadocStyleSheet.css +++ /dev/null @@ -1,59 +0,0 @@ -# originally from http://sensemaya.org/files/stylesheet.css and then modified -# http://sensemaya.org/maya/2009/07/10/making-javadoc-more-legible - -/* Javadoc style sheet */ - -/* Define colors, fonts and other style attributes here to override the defaults */ - -/* Page background color */ -body { background-color: #FFFFFF; color:#333; font-size: 100%; } - -body { font-size: 0.875em; line-height: 1.286em; font-family: "Helvetica", "Arial", sans-serif; } - -code { color: #777; line-height: 1.286em; font-family: "Consolas", "Lucida Console", "Droid Sans Mono", "Andale Mono", "Monaco", "Lucida Sans Typewriter"; } - -a { text-decoration: none; color: #16569A; /* also try #2E85ED, #0033FF, #6C93C6, #1D7BBE, #1D8DD2 */ } -a:hover { text-decoration: underline; } - - -table[border="1"] { border: 1px solid #ddd; } -table[border="1"] td, table[border="1"] th { border: 1px solid #ddd; } -table[cellpadding="3"] td { padding: 0.5em; } - -font[size="-1"] { font-size: 0.85em; line-height: 1.5em; } -font[size="-2"] { font-size: 0.8em; } -font[size="+2"] { font-size: 1.4em; line-height: 1.3em; padding: 0.4em 0; } - -/* Headings */ -h1 { font-size: 1.5em; line-height: 1.286em;} -h2.title { color: #c81f08; } - -/* Table colors */ -.TableHeadingColor { background: #ccc; color:#444; } /* Dark mauve */ -.TableSubHeadingColor { background: #ddd; color:#444; } /* Light mauve */ -.TableRowColor { background: #FFFFFF; color:#666; font-size: 0.95em; } /* White */ -.TableRowColor code { color:#000; } /* White */ - -/* Font used in left-hand frame lists */ -.FrameTitleFont { font-size: 100%; } -.FrameHeadingFont { font-size: 90%; } -.FrameItemFont { font-size: 0.9em; line-height: 1.3em; -} -/* Java Interfaces */ -.FrameItemFont a i { - font-style: normal; color: #16569A; -} -.FrameItemFont a:hover i { - text-decoration: underline; -} - - -/* Navigation bar fonts and colors */ -.NavBarCell1 { background-color:#E0E6DF; } /* Light mauve */ -.NavBarCell1Rev { background-color:#16569A; color:#FFFFFF} /* Dark Blue */ -.NavBarFont1 { } -.NavBarFont1Rev { color:#FFFFFF; } - -.NavBarCell2 { background-color:#FFFFFF; color:#000000} -.NavBarCell3 { background-color:#FFFFFF; color:#000000} - diff --git a/gradle/javadoc_cleanup.gradle b/gradle/javadoc_cleanup.gradle new file mode 100644 index 0000000000..63b4f7f045 --- /dev/null +++ b/gradle/javadoc_cleanup.gradle @@ -0,0 +1,74 @@ +// remove the excessive whitespaces between method arguments in the javadocs +task javadocCleanup(dependsOn: "javadoc") doLast { + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Flowable.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Observable.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Single.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Maybe.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/core/Completable.html')) + + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/flowables/ConnectableFlowable.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/observables/ConnectableObservable.html')) + + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/subjects/ReplaySubject.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/processors/ReplayProcessor.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/subjects/PublishSubject.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/processors/PublishProcessor.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/subjects/AsyncSubject.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/processors/AsyncProcessor.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/subjects/BehaviorSubject.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/processors/BehaviorProcessor.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/processors/MulticastProcessor.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/subjects/UnicastSubject.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/processors/UnicastProcessor.html')) + + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/plugins/RxJavaPlugins.html')) + + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/parallel/ParallelFlowable.html')) + + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/disposables/Disposable.html')) + + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/observers/TestObserver.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/observers/BaseTestConsumer.html')) + fixJavadocFile(rootProject.file('build/docs/javadoc/io/reactivex/rxjava3/subscribers/TestSubscriber.html')) +} + +def fixJavadocFile(file) { + logger.lifecycle("Cleaning up: " + file) + String fileContents = file.getText('UTF-8') + + // lots of spaces after the previous method argument + fileContents = fileContents.replaceAll(",\\s{4,}", ",\n ") + + // lots of spaces after the @NonNull annotations + fileContents = fileContents.replaceAll("@NonNull</a>\\s{4,}", "@NonNull</a> ") + + // lots of spaces after the @Nullable annotations + fileContents = fileContents.replaceAll("@Nullable</a>\\s{4,}", "@Nullable</a> ") + + // javadoc bug: duplicates the link to @NonNull for some reason + def nonNullText1 = "<a href=\"../annotations/NonNull.html\" title=\"annotation in io.reactivex.rxjava3.annotations\">@NonNull</a>" + + fileContents = fileContents.replace(nonNullText1 + " " + nonNullText1, nonNullText1) + fileContents = fileContents.replace(nonNullText1 + "\n " + nonNullText1, nonNullText1) + fileContents = fileContents.replace(nonNullText1 + "\r\n " + nonNullText1, nonNullText1) + + def nonNullText2 = "<a href=\"../../../../io/reactivex/rxjava3/annotations/NonNull.html\" title=\"annotation in io.reactivex.rxjava3.annotations\">@NonNull</a>" + fileContents = fileContents.replace(nonNullText2 + " " + nonNullText2, nonNullText2) + fileContents = fileContents.replace(nonNullText2 + "\n " + nonNullText2, nonNullText2) + fileContents = fileContents.replace(nonNullText2 + "\r\n " + nonNullText2, nonNullText2) + + // javadoc bug: duplicates the link to @Nullable for some reason + def nullableText1 = "<a href=\"../annotations/Nullable.html\" title=\"annotation in io.reactivex.rxjava3.annotations\">@Nullable</a>" + + fileContents = fileContents.replace(nullableText1 + " " + nullableText1, nullableText1) + fileContents = fileContents.replace(nullableText1 + "\n " + nullableText1, nullableText1) + fileContents = fileContents.replace(nullableText1 + "\r\n " + nullableText1, nullableText1) + + def nullableText2 = "<a href=\"../../../../io/reactivex/rxjava3/annotations/Nullable.html\" title=\"annotation in io.reactivex.rxjava3.annotations\">@Nullable</a>" + + fileContents = fileContents.replace(nullableText2 + " " + nullableText2, nullableText2) + fileContents = fileContents.replace(nullableText2 + "\n " + nullableText2, nullableText2) + fileContents = fileContents.replace(nullableText2 + "\r\n " + nullableText2, nullableText2) + + file.setText(fileContents, 'UTF-8') +} diff --git a/gradle/license.gradle b/gradle/license.gradle deleted file mode 100644 index abd2e2c0e1..0000000000 --- a/gradle/license.gradle +++ /dev/null @@ -1,10 +0,0 @@ -// Dependency for plugin was set in buildscript.gradle - -subprojects { -apply plugin: 'license' //nl.javadude.gradle.plugins.license.LicensePlugin -license { - header rootProject.file('codequality/HEADER') - ext.year = Calendar.getInstance().get(Calendar.YEAR) - skipExistingHeaders true -} -} diff --git a/gradle/maven.gradle b/gradle/maven.gradle deleted file mode 100644 index 817846d77f..0000000000 --- a/gradle/maven.gradle +++ /dev/null @@ -1,70 +0,0 @@ -// Maven side of things -subprojects { - apply plugin: 'maven' // Java plugin has to have been already applied for the conf2scope mappings to work - apply plugin: 'signing' - - signing { - required { gradle.taskGraph.hasTask(uploadMavenCentral) } - sign configurations.archives - } - -/** - * Publishing to Maven Central example provided from http://jedicoder.blogspot.com/2011/11/automated-gradle-project-deployment-to.html - * artifactory will execute uploadArchives to force generation of ivy.xml, and we don't want that to trigger an upload to maven - * central, so using custom upload task. - */ -task uploadMavenCentral(type:Upload, dependsOn: signArchives) { - configuration = configurations.archives - onlyIf { ['release', 'snapshot'].contains(project.status) } - repositories.mavenDeployer { - beforeDeployment { signing.signPom(it) } - - // To test deployment locally, use the following instead of oss.sonatype.org - //repository(url: "file://localhost/${rootProject.rootDir}/repo") - - def sonatypeUsername = rootProject.hasProperty('sonatypeUsername')?rootProject.sonatypeUsername:'' - def sonatypePassword = rootProject.hasProperty('sonatypePassword')?rootProject.sonatypePassword:'' - - repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2') { - authentication(userName: sonatypeUsername, password: sonatypePassword) - } - - snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') { - authentication(userName: sonatypeUsername, password: sonatypePassword) - } - - // Prevent datastamp from being appending to artifacts during deployment - uniqueVersion = false - - // Closure to configure all the POM with extra info, common to all projects - pom.project { - name "${project.name}" - description "${project.name} developed by Netflix" - developers { - developer { - id 'netflixgithub' - name 'Netflix Open Source Development' - email 'talent@netflix.com' - } - } - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - distribution 'repo' - } - } - url "https://github.com/Netflix/${rootProject.githubProjectName}" - scm { - connection "scm:git:git@github.com:Netflix/${rootProject.githubProjectName}.git" - url "scm:git:git@github.com:Netflix/${rootProject.githubProjectName}.git" - developerConnection "scm:git:git@github.com:Netflix/${rootProject.githubProjectName}.git" - } - issueManagement { - system 'github' - url "https://github.com/Netflix/${rootProject.githubProjectName}/issues" - } - } - } - } -} diff --git a/gradle/netflix-oss.gradle b/gradle/netflix-oss.gradle deleted file mode 100644 index a87bc54efe..0000000000 --- a/gradle/netflix-oss.gradle +++ /dev/null @@ -1 +0,0 @@ -apply from: 'http://artifacts.netflix.com/gradle-netflix-local/artifactory.gradle' diff --git a/gradle/release.gradle b/gradle/release.gradle deleted file mode 100644 index 7979dc3a18..0000000000 --- a/gradle/release.gradle +++ /dev/null @@ -1,61 +0,0 @@ -apply plugin: 'release' - -[ uploadIvyLocal: 'uploadLocal', uploadArtifactory: 'artifactoryPublish', buildWithArtifactory: 'build' ].each { key, value -> - // Call out to compile against internal repository - task "${key}"(type: GradleBuild) { - startParameter = project.gradle.startParameter.newInstance() - doFirst { - startParameter.projectProperties = [status: project.status, preferredStatus: project.status] - } - startParameter.addInitScript( file('gradle/netflix-oss.gradle') ) - startParameter.getExcludedTaskNames().add('check') - tasks = [ 'build', value ] - } -} - -// Marker task for following code to key in on -task releaseCandidate(dependsOn: release) -task forceCandidate { - onlyIf { gradle.taskGraph.hasTask(releaseCandidate) } - doFirst { project.status = 'candidate' } -} -task forceRelease { - onlyIf { !gradle.taskGraph.hasTask(releaseCandidate) } - doFirst { project.status = 'release' } -} -release.dependsOn([forceCandidate, forceRelease]) - -task uploadMavenCentral(dependsOn: subprojects.tasks.uploadMavenCentral) -task releaseSnapshot(dependsOn: [uploadArtifactory, uploadMavenCentral]) - -// Ensure our versions look like the project status before publishing -task verifyStatus << { - def hasSnapshot = version.contains('-SNAPSHOT') - if (project.status == 'snapshot' && !hasSnapshot) { - throw new GradleException("Version (${version}) needs -SNAPSHOT if publishing snapshot") - } -} -uploadArtifactory.dependsOn(verifyStatus) -uploadMavenCentral.dependsOn(verifyStatus) - -// Ensure upload happens before taggging, hence upload failures will leave repo in a revertable state -preTagCommit.dependsOn([uploadArtifactory, uploadMavenCentral]) - - -gradle.taskGraph.whenReady { taskGraph -> - def hasRelease = taskGraph.hasTask('commitNewVersion') - def indexOf = { return taskGraph.allTasks.indexOf(it) } - - if (hasRelease) { - assert indexOf(build) < indexOf(unSnapshotVersion), 'build target has to be after unSnapshotVersion' - assert indexOf(uploadMavenCentral) < indexOf(preTagCommit), 'preTagCommit has to be after uploadMavenCentral' - assert indexOf(uploadArtifactory) < indexOf(preTagCommit), 'preTagCommit has to be after uploadArtifactory' - } -} - -// Prevent plugin from asking for a version number interactively -ext.'gradle.release.useAutomaticVersion' = "true" - -release { - git.requireBranch = null -} diff --git a/gradle/stylesheet.css b/gradle/stylesheet.css new file mode 100644 index 0000000000..60f1d665bf --- /dev/null +++ b/gradle/stylesheet.css @@ -0,0 +1,573 @@ +/* Javadoc style sheet */ +/* +Overall document style +*/ + +@import url('resources/fonts/dejavu.css'); + +body { + background-color:#ffffff; + color:#353833; + font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size:14px; + margin:0; +} +a:link, a:visited { + text-decoration:none; + color:#4A6782; +} +a:hover, a:focus { + text-decoration:none; + color:#bb7a2a; +} +a:active { + text-decoration:none; + color:#4A6782; +} +a[name] { + color:#353833; +} +a[name]:hover { + text-decoration:none; + color:#353833; +} +pre { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; +} +h1 { + font-size:20px; +} +h2 { + font-size:18px; +} +h3 { + font-size:16px; + font-style:italic; +} +h4 { + font-size:13px; +} +h5 { + font-size:12px; +} +h6 { + font-size:11px; +} +ul { + list-style-type:disc; +} +code, tt { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; + margin-top:8px; + line-height:1.4em; +} +dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; +} +table tr td dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + vertical-align:top; + padding-top:4px; +} +sup { + font-size:8px; +} +/* +Document title and Copyright styles +*/ +.clear { + clear:both; + height:0px; + overflow:hidden; +} +.aboutLanguage { + float:right; + padding:0px 21px; + font-size:11px; + z-index:200; + margin-top:-9px; +} +.legalCopy { + margin-left:.5em; +} +.bar a, .bar a:link, .bar a:visited, .bar a:active { + color:#FFFFFF; + text-decoration:none; +} +.bar a:hover, .bar a:focus { + color:#bb7a2a; +} +.tab { + background-color:#0066FF; + color:#ffffff; + padding:8px; + width:5em; + font-weight:bold; +} +/* +Navigation bar styles +*/ +.bar { + background-color:#4D7A97; + color:#FFFFFF; + padding:.8em .5em .4em .8em; + height:auto;/*height:1.8em;*/ + font-size:11px; + margin:0; +} +.topNav { + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.bottomNav { + margin-top:10px; + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.subNav { + background-color:#dee3e9; + float:left; + width:100%; + overflow:hidden; + font-size:12px; +} +.subNav div { + clear:left; + float:left; + padding:0 0 5px 6px; + text-transform:uppercase; +} +ul.navList, ul.subNavList { + float:left; + margin:0 25px 0 0; + padding:0; +} +ul.navList li{ + list-style:none; + float:left; + padding: 5px 6px; + text-transform:uppercase; +} +ul.subNavList li{ + list-style:none; + float:left; +} +.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { + color:#FFFFFF; + text-decoration:none; + text-transform:uppercase; +} +.topNav a:hover, .bottomNav a:hover { + text-decoration:none; + color:#bb7a2a; + text-transform:uppercase; +} +.navBarCell1Rev { + background-color:#F8981D; + color:#253441; + margin: auto 5px; +} +.skipNav { + position:absolute; + top:auto; + left:-9999px; + overflow:hidden; +} +/* +Page header and footer styles +*/ +.header, .footer { + clear:both; + margin:0 20px; + padding:5px 0 0 0; +} +.indexHeader { + margin:10px; + position:relative; +} +.indexHeader span{ + margin-right:15px; +} +.indexHeader h1 { + font-size:13px; +} +.title { + color:#2c4557; + margin:10px 0; +} +.subTitle { + margin:5px 0 0 0; +} +.header ul { + margin:0 0 15px 0; + padding:0; +} +.footer ul { + margin:20px 0 5px 0; +} +.header ul li, .footer ul li { + list-style:none; + font-size:13px; +} +/* +Heading styles +*/ +div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList li.blockList h3 { + padding:0; + margin:15px 0; +} +ul.blockList li.blockList h2 { + padding:0px 0 20px 0; +} +/* +Page layout container styles +*/ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + clear:both; + padding:10px 20px; + position:relative; +} +.indexContainer { + margin:10px; + position:relative; + font-size:12px; +} +.indexContainer h2 { + font-size:13px; + padding:0 0 3px 0; +} +.indexContainer ul { + margin:0; + padding:0; +} +.indexContainer ul li { + list-style:none; + padding-top:2px; +} +.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { + font-size:12px; + font-weight:bold; + margin:10px 0 0 0; + color:#4E4E4E; +} +.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { + /* margin:5px 0 10px 0px; */ + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} +.serializedFormContainer dl.nameValue dt { + margin-left:1px; + font-size:1.1em; + display:inline; + font-weight:bold; +} +.serializedFormContainer dl.nameValue dd { + margin:0 0 0 1px; + font-size:1.1em; + display:inline; +} +/* +List styles +*/ +ul.horizontal li { + display:inline; + font-size:0.9em; +} +ul.inheritance { + margin:0; + padding:0; +} +ul.inheritance li { + display:inline; + list-style:none; +} +ul.inheritance li ul.inheritance { + margin-left:15px; + padding-left:15px; + padding-top:1px; +} +ul.blockList, ul.blockListLast { + margin:10px 0 10px 0; + padding:0; +} +ul.blockList li.blockList, ul.blockListLast li.blockList { + list-style:none; + margin-bottom:15px; + line-height:1.4; +} +ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { + padding:0px 20px 5px 10px; + border:1px solid #ededed; + background-color:#f8f8f8; +} +ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { + padding:0 0 5px 8px; + background-color:#ffffff; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { + margin-left:0; + padding-left:0; + padding-bottom:15px; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { + list-style:none; + border-bottom:none; + padding-bottom:0; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top:0; + margin-bottom:1px; +} +/* +Table styles +*/ +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { + width:100%; + border-left:1px solid #EEE; + border-right:1px solid #EEE; + border-bottom:1px solid #EEE; +} +.overviewSummary, .memberSummary { + padding:0px; +} +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption { + position:relative; + text-align:left; + background-repeat:no-repeat; + color:#253441; + font-weight:bold; + clear:none; + overflow:hidden; + padding:0px; + padding-top:10px; + padding-left:1px; + margin:0px; + white-space:pre; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { + color:#FFFFFF; +} +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + padding-bottom:7px; + display:inline-block; + float:left; + background-color:#F8981D; + border: none; + height:16px; +} +.memberSummary caption span.activeTableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#F8981D; + height:16px; +} +.memberSummary caption span.tableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#4D7A97; + height:16px; +} +.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { + padding-top:0px; + padding-left:0px; + padding-right:0px; + background-image:none; + float:none; + display:inline; +} +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { + display:none; + width:5px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .activeTableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .tableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + background-color:#4D7A97; + float:left; + +} +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td { + text-align:left; + padding:0px 0px 12px 10px; +} +th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, +td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ + vertical-align:top; + padding-right:0px; + padding-top:8px; + padding-bottom:3px; +} +th.colFirst, th.colLast, th.colOne, .constantsSummary th { + background:#dee3e9; + text-align:left; + padding:8px 3px 3px 7px; +} +td.colFirst, th.colFirst { + white-space:nowrap; + font-size:13px; +} +td.colLast, th.colLast { + font-size:13px; +} +td.colOne, th.colOne { + font-size:13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.useSummary td.colFirst, .useSummary th.colFirst, +.overviewSummary td.colOne, .overviewSummary th.colOne, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colOne, .memberSummary th.colOne, +.typeSummary td.colFirst{ + width:25%; + vertical-align:top; +} +td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { + font-weight:bold; +} +.tableSubHeadingColor { + background-color:#EEEEFF; +} +.altColor { + background-color:#FFFFFF; +} +.rowColor { + background-color:#EEEEEF; +} +/* +Content styles +*/ +.description pre { + margin-top:0; +} +.deprecatedContent { + margin:0; + padding:10px 0; +} +.docSummary { + padding:0; +} + +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style:normal; +} + +div.block { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} + +td.colLast div { + padding-top:0px; +} + +td.colLast a { + padding-bottom:3px; +} +/* +Formatting effect styles +*/ +.sourceLineNo { + color:green; + padding:0 30px 0 0; +} +h1.hidden { + visibility:hidden; + overflow:hidden; + font-size:10px; +} +.block { + display:block; + margin:3px 10px 2px 0px; + color:#474747; +} +.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, +.overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, +.seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { + font-weight:bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style:italic; +} + +div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style:normal; +} + +div.contentContainer ul.blockList li.blockList h2{ + padding-bottom:0px; +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a7634b071c..afba109285 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 19fa1710c4..3c44eb1b6f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Feb 05 12:05:54 CET 2014 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.8-all.zip diff --git a/gradlew b/gradlew index 91a7e269e1..65dcd68d65 100755 --- a/gradlew +++ b/gradlew @@ -1,79 +1,129 @@ -#!/usr/bin/env bash +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -82,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -90,75 +140,105 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282aa6..93e3f59f13 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,4 +1,20 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -8,20 +24,24 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +55,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,44 +65,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/language-adaptors/README.md b/language-adaptors/README.md deleted file mode 100644 index 6c219e0b4b..0000000000 --- a/language-adaptors/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Language Adaptors - -RxJava includes language adaptor submodules. These modules are additive to the rxjava-core library in the classpath. - -For example, to use the Scala adaptor you would have the rxjava-core-x.y.z.jar and rxjava-scala-x-y-z.jar. - -If there is a language you'd like supported please look at the existing adaptors (such as <a href="https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-groovy">Groovy</a>) and implement the adaptor for your language of choice. - -If you feel it would be valuable for the community submit a pull request and we'll accept it into the main project. - -Please comply with the conventions established by the existing language adaptors if you intend to submit a pull request. diff --git a/language-adaptors/rxjava-clojure/README.md b/language-adaptors/rxjava-clojure/README.md deleted file mode 100644 index e3729bdc94..0000000000 --- a/language-adaptors/rxjava-clojure/README.md +++ /dev/null @@ -1,154 +0,0 @@ -Clojure bindings for RxJava. - -# 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-clojure%22). - -Example for Leiningen: - -```clojure -[com.netflix.rxjava/rxjava-clojure "x.y.z"] -``` - -and for Gradle: - -```groovy -compile 'com.netflix.rxjava:rxjava-clojure:x.y.z' -``` - -and for Maven: - -```xml -<dependency> - <groupId>com.netflix.rxjava</groupId> - <artifactId>rxjava-clojure</artifactId> - <version>x.y.z</version> -</dependency> -``` - -and for Ivy: - -```xml -<dependency org="com.netflix.rxjava" name="rxjava-clojure" rev="x.y.z" /> -``` - -# Clojure Bindings -This library provides convenient, idiomatic Clojure bindings for RxJava. - -The bindings try to present an API that will be comfortable and familiar to a Clojure programmer that's familiar with the sequence operations in `clojure.core`. It "fixes" several issues with using RxJava with raw Java interop, for example: - -* Argument lists are in the "right" order. So in RxJava, the function applied in `Observable.map` is the second argument, while here it's the first argument with one or more Observables as trailing arguments -* Operators take normal Clojure functions as arguments, bypassing need for the interop described below -* Predicates accomodate Clojure's notion of truth -* Operators are generally names as they would be in `clojure.core` rather than the Rx names - -There is no object wrapping going on. That is, all functions return normal `rx.Observable` objects, so you can always drop back to Java interop for anything that's missing in this wrapper. - -## Basic Usage -Most functionality resides in the `rx.lang.clojure.core` namespace and for the most part looks like normal Clojure sequence manipulation: - -```clojure -(require '[rx.lang.clojure.core :as rx]) - -(->> my-observable - (rx/map (comp clojure.string/lower-case :first-name)) - (rx/map clojure.string/lower-case) - (rx/filter #{"bob"}) - (rx/distinct) - (rx/into [])) -;=> An Observable that emits a single vector of names -``` - -Blocking operators, which are useful for testing, but should otherwise be avoided, reside in `rx.lang.clojure.blocking`. For example: - -```clojure -(require '[rx.lang.clojure.blocking :as rxb]) - -(rxb/doseq [{:keys [first-name]} users-observable] - (println "Hey," first-name)) -;=> nil -``` - -## Open Issues - -* The missing stuff mentioned below -* `group-by` val-fn variant isn't implemented in RxJava -* There are some functions for defining customer Observables and Operators (`subscriber`, `operator*`, `observable*`). I don't think these are really enough for serious operator implementation, but I'm hesitant to guess at an abstraction at this point. These will probably change dramatically. - -## What's Missing -This library is an ongoing work in progress driven primarily by the needs of one team at Netflix. As such some things are currently missing: - -* Highly-specific operators that we felt cluttered the API and were easily composed from existing operators, especially since we're in not-Java land. For example, `Observable.sumLong()`. -* Most everything involving schedulers -* Most everything involving time -* `Observable.window` and `Observable.buffer`. Who knows which parts of these beasts to wrap? - -Of course, contributions that cover these cases are welcome. - -# Low-level Interop -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. - -## Basic Usage - -### Requiring the interop namespace -The first thing to do is to require the namespace: - -```clojure -(ns my.namespace - (:require [rx.lang.clojure.interop :as rx]) - (:import [rx Observable])) -``` - -or, at the REPL: - -```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.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.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.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")))) -``` - -### Using Observable/create -As of 0.17, `rx.Observable/create` takes an implementation of `rx.Observable$OnSubscribe` which is basically an alias for `rx.functions.Action1` that takes an `rx.Subscriber` as its argument. Thus, you can just use `rx/action` when creating new observables: - -```clojure -; A simple observable that emits 0..9 taking unsubscribe into account -(Observable/create (rx/action [^rx.Subscriber s] - (loop [i 0] - (when (and (< i 10) (.isUnsubscribed s)) - (.onNext s i) - (recur (inc i)))) - (.onCompleted s))) -``` - -## 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`. - diff --git a/language-adaptors/rxjava-clojure/build.gradle b/language-adaptors/rxjava-clojure/build.gradle deleted file mode 100644 index bfbcf395b6..0000000000 --- a/language-adaptors/rxjava-clojure/build.gradle +++ /dev/null @@ -1,75 +0,0 @@ -apply plugin: 'clojure' -apply plugin: 'osgi' - -dependencies { - compile project(':rxjava-core') - - // clojure - compile 'org.clojure:clojure:1.4.+' - - // this should be 'compile' for the 'examples' module - testCompile 'clj-http:clj-http:0.6.4' // https://clojars.org/clj-http -} - -tasks.compileExamplesClojure.classpath = files(tasks.compileClojure.destinationDir) + tasks.compileClojure.classpath + configurations.testCompile - -/* - * Clojure - */ -aotCompile = true -warnOnReflection = false - -buildscript { - repositories { maven { url "http://clojars.org/repo" } } - dependencies { classpath "clojuresque:clojuresque:1.5.8" } -} - -repositories { - clojarsRepo() -} - -/* - * Add Counterclockwise and include 'testCompile' dependencies - */ -eclipse { - project { - natures "ccw.nature" - } -} - -tasks.clojureTest { - classpath = classpath + configurations.testCompile -} - -jar { - manifest { - name = 'rxjava-clojure' - 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' - } -} - - -//////////////////////////////////////////////////////////////////////////////// -// Define a task that runs an nrepl server. The port is given with the nreplPort -// property: -// gradlew nrepl -PnreplPort=9999 -// or put the property in ~/.gradle/gradle.properties - -def nreplPortValue = (project.hasProperty('nreplPort') && !project.nreplPort.isEmpty()) ? project.nreplPort : '9999' -configurations { nrepl } -dependencies { nrepl 'org.clojure:tools.nrepl:0.2.2' } -task nrepl(type: JavaExec) { - classpath configurations.nrepl, - project.sourceSets.main.clojure.srcDirs, - project.sourceSets.test.clojure.srcDirs, - sourceSets.main.runtimeClasspath, - sourceSets.test.runtimeClasspath - - main = "clojure.main" - args '--eval', "(ns gradle-nrepl (:require [clojure.tools.nrepl.server :refer (start-server stop-server)]))", - '--eval', "(println \"Starting nrepl server on port $nreplPortValue\")", - '--eval', "(def server (start-server :port $nreplPortValue))" -} diff --git a/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/http_examples.clj b/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/http_examples.clj deleted file mode 100644 index 958d527486..0000000000 --- a/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/http_examples.clj +++ /dev/null @@ -1,73 +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. -; -(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<String> of HTML" - (Observable/create - (rx/action [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 - (.add observer (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<String> of HTML" - (Observable/create - (rx/action [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 - (.add observer (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.clj deleted file mode 100644 index af0d7328c0..0000000000 --- a/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/rx_examples.clj +++ /dev/null @@ -1,215 +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. -; -(ns rx.lang.clojure.examples.rx-examples - (: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 -; different language implementations in-sync for easy comparison. - -; -------------------------------------------------- -; Hello World! -; -------------------------------------------------- - -(defn hello - [& args] - (-> - ; type hint required due to `Observable/from` overloading - (Observable/from ^java.lang.Iterable args) - (.subscribe (rx/action [v] (println (str "Hello " v "!")))))) - -; To see output -(comment - (hello "Ben" "George")) - -; -------------------------------------------------- -; Create Observable from Existing Data -; -------------------------------------------------- - - -(defn existingDataFromNumbersUsingFrom [] - (Observable/from [1 2 3 4 5 6])) - -(defn existingDataFromObjectsUsingFrom [] - (Observable/from ["a" "b" "c"])) - -(defn existingDataFromListUsingFrom [] - (let [list [5, 6, 7, 8]] - (Observable/from list))) - -(defn existingDataWithJust [] - (Observable/just "one object")) - -; -------------------------------------------------- -; Custom Observable -; -------------------------------------------------- - -(defn customObservable - "This example shows a custom Observable. Note the - .isUnsubscribed check so that it can be stopped early. - - returns Observable<String>" - [] - (Observable/create - (rx/action [^rx.Subscriber s] - (loop [x (range 50)] - (when (and (not (.isUnsubscribed s)) x) - ; TODO - (println "HERE " (.isUnsubscribed s) (first x)) - (-> s (.onNext (str "value_" (first x)))) - (recur (next x)))) - ; after sending all values we complete the sequence - (-> s .onCompleted)))) - -; To see output -(comment - (.subscribe (customObservable) (rx/action* println))) - -; -------------------------------------------------- -; Composition - Simple -; -------------------------------------------------- - -(defn simpleComposition - "Calls 'customObservable' and defines - a chain of operators to apply to the callback sequence." - [] - (-> - (customObservable) - (.skip 10) - (.take 5) - (.map (rx/fn [v] (str v "_transformed"))) - (.subscribe (rx/action [v] (println "onNext =>" v))))) - -; To see output -(comment - (simpleComposition)) - - -; -------------------------------------------------- -; Composition - Multiple async calls combined -; -------------------------------------------------- - -(defn getUser - "Asynchronously fetch user data - - return Observable<Map>" - [userId] - (Observable/create - (rx/action [^rx.Subscriber s] - (let [f (future - (try - ; simulate fetching user data via network service call with latency - (Thread/sleep 60) - (-> s (.onNext {:user-id userId - :name "Sam Harris" - :preferred-language (if (= 0 (rand-int 2)) "en-us" "es-us") })) - (-> s .onCompleted) - (catch Exception e - (-> s (.onError e))))) ] - ; a subscription that cancels the future if unsubscribed - (.add s (Subscriptions/create (rx/action [] (future-cancel f)))))))) - -(defn getVideoBookmark - "Asynchronously fetch bookmark for video - - return Observable<Integer>" - [userId, videoId] - (Observable/create - (rx/action [^rx.Subscriber s] - (let [f (future - (try - ; simulate fetching user data via network service call with latency - (Thread/sleep 20) - (-> s (.onNext {:video-id videoId - ; 50/50 chance of giving back position 0 or 0-2500 - :position (if (= 0 (rand-int 2)) 0 (rand-int 2500))})) - (-> s .onCompleted) - (catch Exception e - (-> s (.onError e)))))] - ; a subscription that cancels the future if unsubscribed - (.add s (Subscriptions/create (rx/action [] (future-cancel f)))))))) - -(defn getVideoMetadata - "Asynchronously fetch movie metadata for a given language - return Observable<Map>" - [videoId, preferredLanguage] - (Observable/create - (rx/action [^rx.Subscriber s] - (let [f (future - (println "getVideoMetadata " videoId) - (try - ; simulate fetching video data via network service call with latency - (Thread/sleep 50) - ; contrived metadata for en-us or es-us - (if (= "en-us" preferredLanguage) - (-> s (.onNext {:video-id videoId - :title "House of Cards: Episode 1" - :director "David Fincher" - :duration 3365}))) - (if (= "es-us" preferredLanguage) - (-> s (.onNext {:video-id videoId - :title "Cámara de Tarjetas: Episodio 1" - :director "David Fincher" - :duration 3365}))) - (-> s .onCompleted) - (catch Exception e - (-> s (.onError e))))) ] - ; a subscription that cancels the future if unsubscribed - (.add s (Subscriptions/create (rx/action [] (future-cancel f)))))))) - - -(defn getVideoForUser [userId videoId] - "Get video metadata for a given userId - - video metadata - - video bookmark position - - user data - return Observable<Map>" - (let [user-observable (-> (getUser userId) - (.map (rx/fn [user] - {:user-name (:name user) - :language (:preferred-language user)}))) - bookmark-observable (-> (getVideoBookmark userId videoId) - (.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 - (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 - (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 (rx/fn [data] - {:video-id videoId - :video-metadata (:metadata-map data) - :user-id userId - :language (:language (:user-map data)) - :bookmark (:viewed-position (:bookmark-map data)) }))))) - -; To see output like this: -; {:video-id 78965, :video-metadata {:video-id 78965, :title Cámara de Tarjetas: Episodio 1, -; :director David Fincher, :duration 3365}, :user-id 12345, :language es-us, :bookmark 0} -; -(comment - (-> (getVideoForUser 12345 78965) - (.toBlockingObservable) - .single)) - 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.clj deleted file mode 100644 index 11078aad22..0000000000 --- a/language-adaptors/rxjava-clojure/src/examples/clojure/rx/lang/clojure/examples/video_example.clj +++ /dev/null @@ -1,194 +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. -; -(ns rx.lang.clojure.examples.video-example - (: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 - -(declare get-video-grid-for-display) -(declare get-list-of-lists) -(declare video-list) -(declare video-list->videos) -(declare video->metadata) -(declare video->bookmark) -(declare video->rating) - -; just use a simple lock to keep multi-threaded output from being interleaved -(def print-lock (Object.)) - -(defn example1 - [on-complete] - (println "Starting example 1") - ; this will print the dictionary for each video and is a good representation of - ; how progressive rendering could work - (println "---- sequence of video dictionaries ----") - (-> (get-video-grid-for-display 1) - (.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] - (println "Starting example 2") - ; onNext will be called once with a list and demonstrates how a sequence can be combined - ; for document style responses (most webservices) - (-> (get-video-grid-for-display 1) - .toList - (.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))))) - -(defn -main - [& args] - ; Run example1 followed by example2, then exit - (example1 (fn [] (example2 #(System/exit 0))))) - -(defn ^Observable get-video-grid-for-display - " - Demonstrate how Rx is used to compose Observables together such as - how a web service would to generate a JSON response. - - The simulated methods for the metadata represent different services - that are often backed by network calls. - - This will return a sequence of maps like this: - - {:id 1000, :title video-1000-title, :length 5428, :bookmark 0, - :rating {:actual 4 :average 3 :predicted 0}} - " - [user-id] - (-> (get-list-of-lists user-id) - (.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 (rx/fn [video] - ; for each video we want to fetch metadata - (let [m (-> (video->metadata video) - (.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 (rx/fn [position] - {:bookmark position}))) - r (-> (video->rating video user-id) - (.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 (rx/fn [m b r] - (merge {:id video} m b r))))))))))) - - -; A little helper to make the future-based observables a little less verbose -; this has possibilities ... -(defn- ^Observable future-observable - "Returns an observable that executes (f observer) in a future, returning a - subscription that will cancel the future." - [f] - (Observable/create (rx/action [^rx.Subscriber s] - (println "Starting f") - (let [f (future (f s))] - (.add s (Subscriptions/create (rx/action [] (future-cancel f)))))))) - -(defn ^Observable get-list-of-lists - " - Retrieve a list of lists of videos (grid). - - Observable<VideoList> is the \"push\" equivalent to List<VideoList> - " - [user-id] - (future-observable (fn [^rx.Subscriber s] - (Thread/sleep 180) - (dotimes [i 15] - (.onNext s (video-list i))) - (.onCompleted s)))) - - -(comment (-> (get-list-of-lists 7777) - .toList - .toBlockingObservable - .single)) - -(defn video-list - [position] - {:position position - :name (str "ListName-" position) }) - -(defn ^Observable video-list->videos - [{:keys [position] :as video-list}] - (Observable/create (rx/action [^rx.Subscriber s] - (dotimes [i 50] - (.onNext s (+ (* position 1000) i))) - (.onCompleted s)))) - -(comment (-> (video-list->videos (video-list 2)) - .toList - .toBlockingObservable - .single)) - -(defn ^Observable video->metadata - [video-id] - (Observable/create (rx/action [^rx.Subscriber s] - (.onNext s {:title (str "video-" video-id "-title") - :actors ["actor1" "actor2"] - :duration 5428 }) - (.onCompleted s)))) - -(comment (-> (video->metadata 10) - .toList - .toBlockingObservable - .single)) - -(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 (-> (video->bookmark 112345 99999) - .toList - .toBlockingObservable - .single)) - -(defn ^Observable video->rating - [video-id user-id] - (future-observable (fn [^Observer observer] - (Thread/sleep 10) - (.onNext observer {:video-id video-id - :user-id user-id - :predicted-star-rating (rand-int 5) - :average-star-rating (rand-int 5) - :actual-star-rating (rand-int 5) }) - (.onCompleted observer)))) - -(comment (-> (video->rating 234345 8888) - .toList - .toBlockingObservable - .single)) - diff --git a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/blocking.clj b/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/blocking.clj deleted file mode 100644 index feee933225..0000000000 --- a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/blocking.clj +++ /dev/null @@ -1,140 +0,0 @@ -(ns rx.lang.clojure.blocking - "Blocking operators and functions. These should never be used in - production code except at the end of an async chain to convert from - rx land back to sync land. For example, to produce a servlet response. - - If you use these, you're a bad person. - " - (:refer-clojure :exclude [first into doseq last]) - (:require [rx.lang.clojure.interop :as iop] [rx.lang.clojure.core :as rx]) - (:import [rx Observable] - [rx.observables BlockingObservable])) - -(def ^:private -ns- *ns*) -(set! *warn-on-reflection* true) - -(defmacro ^:private with-ex-unwrap - "The blocking ops wrap errors stuff in RuntimeException because of stupid Java. - This tries to unwrap them so callers get the exceptions they expect." - [& body] - `(try - ~@body - (catch RuntimeException e# - (throw (or - (and (identical? RuntimeException (class e#)) - (.getCause e#)) - e#))))) - -(defn ^BlockingObservable ->blocking - "Convert an Observable to a BlockingObservable. - - If o is already a BlockingObservable it's returned unchanged. - " - [o] - (if (instance? BlockingObservable o) - o - (.toBlockingObservable ^Observable o))) - -(defn o->seq - "Returns a lazy sequence of the items emitted by o - - See: - rx.observables.BlockingObservable/getIterator - rx.lang.clojure.core/seq->o - " - [o] - (-> (->blocking o) - (.getIterator) - (iterator-seq))) - -(defn first - "*Blocks* and waits for the first value emitted by the given observable. - - If the Observable is empty, returns nil - - If an error is produced it is thrown. - - See: - clojure.core/first - rx/first - rx.observables.BlockingObservable/first - " - [observable] - (with-ex-unwrap - (.firstOrDefault (->blocking observable) nil))) - -(defn last - "*Blocks* and waits for the last value emitted by the given observable. - - If the Observable is empty, returns nil - - If an error is produced it is thrown. - - See: - clojure.core/last - rx/last - rx.observable.BlockingObservable/last - " - [observable] - (with-ex-unwrap - (.lastOrDefault (->blocking observable) nil))) - -(defn single - "*Blocks* and waits for the first value emitted by the given observable. - - An error is thrown if zero or more then one value is produced. - " - [observable] - (with-ex-unwrap - (.single (->blocking observable)))) - -(defn into - "*Blocks* and pours the elements emitted by the given observables into - to. - - If an error is produced it is thrown. - - See: - clojure.core/into - rx/into - " - [to from-observable] - (with-ex-unwrap - (clojure.core/into to (o->seq from-observable)))) - -(defn doseq* - "*Blocks* and executes (f x) for each x emitted by xs - - Returns nil. - - See: - doseq - clojure.core/doseq - " - [xs f] - (with-ex-unwrap - (-> (->blocking xs) - (.forEach (rx.lang.clojure.interop/action* f))))) - -(defmacro doseq - "Like clojure.core/doseq except iterates over an observable in a blocking manner. - - Unlike clojure.core/doseq, only supports a single binding - - Returns nil. - - Example: - - (rx-blocking/doseq [{:keys [name]} users-observable] - (println \"User:\" name)) - - See: - doseq* - clojure.core/doseq - " - [bindings & body] - (when (not= (count bindings) 2) - (throw (IllegalArgumentException. (str "sorry, rx/doseq only supports one binding")))) - (let [[k v] bindings] - `(doseq* ~v (fn [~k] ~@body)))) - diff --git a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/chunk.clj b/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/chunk.clj deleted file mode 100644 index ec07e51055..0000000000 --- a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/chunk.clj +++ /dev/null @@ -1,100 +0,0 @@ -(ns rx.lang.clojure.chunk - (:refer-clojure :exclude [chunk]) - (:require [rx.lang.clojure.core :as rx])) - -(def ^:private -ns- *ns*) -(set! *warn-on-reflection* true) - -(defn chunk - "EXTREMELY EXPERIMENTAL AND SUBJECT TO CHANGE OR DELETION - - TODO RxJava's much bigger since this was written. Is there something built in? - - Same as rx.Observable.merge(Observable<Observable<T>>) but the input Observables - are \"chunked\" so that at most chunk-size of them are \"in flight\" at any given - time. - - The order of the input Observables is not preserved. - - The main purpose here is to allow a large number of Hystrix observables to - be processed in a controlled way so that the Hystrix execution queues aren't - overwhelmed. - - Example: - - (->> users - (rx/map #(-> (GetUserCommand. %) .toObservable)) - (chunk 10)) - - See: - http://netflix.github.io/RxJava/javadoc/rx/Observable.html#merge(rx.Observable) - http://netflix.github.io/RxJava/javadoc/rx/Observable.html#mergeDelayError(rx.Observable) - " - ([chunk-size observable-source] (chunk chunk-size {} observable-source)) - ([chunk-size options observable-source] - (let [new-state-atom #(atom {:in-flight #{} ; observables currently in-flight - :buffered [] ; observables waiting to be emitted - :complete false ; true if observable-source is complete - :observer % }) ; the observer - ps #(do (printf "%s/%d/%d%n" - (:complete %) - (-> % :buffered count) - (-> % :in-flight count)) - (flush)) - - ; Given the current state, returns [action new-state]. action is the - ; next Observable or Throwable to emit, or :complete if we're done. - next-state (fn [{:keys [complete buffered in-flight] :as old}] - (cond - (empty? buffered) [complete old] - - (< (count in-flight) chunk-size) (let [next-o (first buffered)] - [next-o - (-> old - (update-in [:buffered] next) - (update-in [:in-flight] conj next-o))]) - - :else [nil old])) - - ; Advance the state, performing side-effects as necessary - advance! (fn advance! [state-atom] - (let [old-state @state-atom - [action new-state] (next-state old-state)] - (if (compare-and-set! state-atom old-state new-state) - (let [observer (:observer new-state)] - (if (:debug options) (ps new-state)) - (cond - (= :complete action) - (rx/on-completed observer) - - (instance? Throwable action) - (rx/on-error observer action) - - (instance? rx.Observable action) - (rx/on-next observer - (.finallyDo ^rx.Observable action - (reify rx.functions.Action0 - (call [this] - (swap! state-atom update-in [:in-flight] disj action) - (advance! state-atom))))))) - (recur state-atom)))) - - subscribe (fn [state-atom] - (rx/subscribe observable-source - (fn [o] - (swap! state-atom update-in [:buffered] conj o) - (advance! state-atom)) - - (fn [e] - (swap! state-atom assoc :complete e) - (advance! state-atom)) - - (fn [] - (swap! state-atom assoc :complete :complete) - (advance! state-atom)))) - observable (rx/observable* (fn [observer] - (subscribe (new-state-atom observer)))) ] - (if (:delay-error? options) - (rx.Observable/mergeDelayError observable) - (rx.Observable/merge observable))))) - diff --git a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/core.clj b/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/core.clj deleted file mode 100644 index 9af3891494..0000000000 --- a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/core.clj +++ /dev/null @@ -1,962 +0,0 @@ -(ns rx.lang.clojure.core - (:refer-clojure :exclude [concat cons count cycle - distinct do drop drop-while - empty every? - filter first future - group-by - interleave interpose into iterate - keep keep-indexed - map mapcat map-indexed - merge next nth partition-all - range reduce reductions - rest seq some sort sort-by split-with - take take-while throw]) - (:require [rx.lang.clojure.interop :as iop] - [rx.lang.clojure.graph :as graph] - [rx.lang.clojure.realized :as realized]) - (:import [rx - Observable - Observer Observable$Operator Observable$OnSubscribe - Subscriber Subscription] - [rx.observables - BlockingObservable - GroupedObservable] - [rx.subscriptions Subscriptions] - [rx.functions Action0 Action1 Func0 Func1 Func2])) - -(set! *warn-on-reflection* true) - -(declare concat* concat map* map map-indexed reduce take take-while) - -(defn ^Func1 fn->predicate - "Turn f into a predicate that returns true/false like Rx predicates should" - [f] - (iop/fn* (comp boolean f))) - -;################################################################################ - -(defn observable? - "Returns true if o is an rx.Observable" - [o] - (instance? Observable o)) - -;################################################################################ - -(defn on-next - "Call onNext on the given observer and return o." - [^Observer o value] - (.onNext o value) - o) - -(defn on-completed - "Call onCompleted on the given observer and return o." - [^Observer o] - (.onCompleted o) - o) - -(defn on-error - "Call onError on the given observer and return o." - [^Observer o e] - (.onError o e) - o) - -(defmacro catch-error-value - "Experimental - - TODO: Better name, better abstraction. - - Evaluate body and return its value. If an exception e is thrown, inject the - given value into the exception's cause and call (on-error error-observer e), - returning e. - - This is meant to facilitate implementing Observers that call user-supplied code - safely. The general pattern is something like: - - (fn [o v] - (rx/catch-error-value o v - (rx/on-next o (some-func v)))) - - If (some-func v) throws an exception, it is caught, v is injected into the - exception's cause (with OnErrorThrowable/addValueAsLastCause) and - (rx/on-error o e) is invoked. - - See: - rx.exceptions.OnErrorThrowable/addValueAsLastCause - " - [error-observer value & body] - `(try - ~@body - (catch Throwable e# - (on-error ~error-observer - (rx.exceptions.OnErrorThrowable/addValueAsLastCause e# ~value)) - e#))) - -;################################################################################ -; Tools for creating new operators and observables - -(declare unsubscribed?) - -(defn ^Subscriber subscriber - "Experimental, subject to change or deletion." - ([o on-next-action] (subscriber o on-next-action nil nil)) - ([o on-next-action on-error-action] (subscriber o on-next-action on-error-action nil)) - ([^Subscriber o on-next-action on-error-action on-completed-action] - (proxy [Subscriber] [o] - (onCompleted [] - (if on-completed-action - (on-completed-action o) - (on-completed o))) - (onError [e] - (if on-error-action - (on-error-action o e) - (on-error o e))) - (onNext [t] - (if on-next-action - (on-next-action o t) - (on-next o t)))))) - -(defn ^Subscription subscription - "Create a new subscription that calls the given no-arg handler function when - unsubscribe is called - - See: - rx.subscriptions.Subscriptions/create - " - [handler] - (Subscriptions/create ^Action0 (iop/action* handler))) - -(defn ^Observable$Operator operator* - "Experimental, subject to change or deletion. - - Returns a new implementation of rx.Observable$Operator that calls the given - function with a rx.Subscriber. The function should return a rx.Subscriber. - - See: - lift - rx.Observable$Operator - " - [f] - {:pre [(fn? f)]} - (reify Observable$Operator - (call [this o] - (f o)))) - -(defn ^Observable observable* - "Create an Observable from the given function. - - When subscribed to, (f subscriber) is called at which point, f can start emitting values, etc. - The passed subscriber is of type rx.Subscriber. - - See: - rx.Subscriber - rx.Observable/create - " - [f] - (Observable/create ^Observable$OnSubscribe (iop/action* f))) - -(defn wrap-on-completed - "Wrap handler with code that automaticaly calls rx.Observable.onCompleted." - [handler] - (fn [^Observer observer] - (handler observer) - (when-not (unsubscribed? observer) - (.onCompleted observer)))) - -(defn wrap-on-error - "Wrap handler with code that automaticaly calls (on-error) if an exception is thrown" - [handler] - (fn [^Observer observer] - (try - (handler observer) - (catch Throwable e - (when-not (unsubscribed? observer) - (.onError observer e)))))) - -(defn lift - "Lift the Operator op over the given Observable xs - - Example: - - (->> my-observable - (rx/lift (rx/operator ...)) - ...) - - See: - rx.Observable/lift - operator - " - [^Observable$Operator op ^Observable xs] - (.lift xs op)) - -;################################################################################ - -(defn ^Subscription subscribe - "Subscribe to the given observable. - - on-X-action is a normal clojure function. - - See: - rx.Observable/subscribe - " - - ([^Observable o on-next-action] - (.subscribe o - ^Action1 (iop/action* on-next-action))) - - ([^Observable o on-next-action on-error-action] - (.subscribe o - ^Action1 (iop/action* on-next-action) - ^Action1 (iop/action* on-error-action))) - - ([^Observable o on-next-action on-error-action on-completed-action] - (.subscribe o - ^Action1 (iop/action* on-next-action) - ^Action1 (iop/action* on-error-action) - ^Action0 (iop/action* on-completed-action)))) - -(defn unsubscribe - "Unsubscribe from Subscription s and return it." - [^Subscription s] - (.unsubscribe s) - s) - -(defn subscribe-on - "Cause subscriptions to the given observable to happen on the given scheduler. - - Returns a new Observable. - - See: - rx.Observable/subscribeOn - " - [^rx.Scheduler s ^Observable xs] - (.subscribeOn xs s)) - -(defn unsubscribe-on - "Cause unsubscriptions from the given observable to happen on the given scheduler. - - Returns a new Observable. - - See: - rx.Observable/unsubscribeOn - " - [^rx.Scheduler s ^Observable xs] - (.unsubscribeOn xs s)) - -(defn unsubscribed? - "Returns true if the given Subscription (or Subscriber) is unsubscribed. - - See: - rx.Observable/create - observable* - " - [^Subscription s] - (.isUnsubscribed s)) - -;################################################################################ -; Functions for creating Observables - -(defn ^Observable never - "Returns an Observable that never emits any values and never completes. - - See: - rx.Observable/never - " - [] - (Observable/never)) - -(defn ^Observable empty - "Returns an Observable that completes immediately without emitting any values. - - See: - rx.Observable/empty - " - [] - (Observable/empty)) - -(defn ^Observable return - "Returns an observable that emits a single value. - - See: - rx.Observable/just - " - [value] - (Observable/just value)) - -(defn ^Observable seq->o - "Make an observable out of some seq-able thing. The rx equivalent of clojure.core/seq." - [xs] - (if-let [s (clojure.core/seq xs)] - (Observable/from ^Iterable s) - (empty))) - -;################################################################################ -; Operators - -(defn serialize - "Serialize execution. - - See: - rx.Observable/serialize - " - ([^Observable xs] - (.serialize xs))) - -(defn merge* - "Merge an Observable of Observables into a single Observable - - If you want clojure.core/merge, it's just this: - - (rx/reduce clojure.core/merge {} maps) - - See: - merge - merge-delay-error* - rx.Observable/merge - " - [^Observable xs] - (Observable/merge xs)) - -(defn ^Observable merge - "Merge one or more Observables into a single observable. - - If you want clojure.core/merge, it's just this: - - (rx/reduce clojure.core/merge {} maps) - - See: - merge* - merge-delay-error - rx.Observable/merge - " - [& os] - (merge* (seq->o os))) - -(defn ^Observable merge-delay-error* - "Same as merge*, but all values are emitted before errors are propagated" - [^Observable xs] - (Observable/mergeDelayError xs)) - -(defn ^Observable merge-delay-error - "Same as merge, but all values are emitted before errors are propagated" - [& os] - (merge-delay-error* (seq->o os))) - -(defn cache - "caches the observable value so that multiple subscribers don't re-evaluate it. - - See: - rx.Observable/cache" - [^Observable xs] - (.cache xs)) - -(defn cons - "cons x to the beginning of xs" - [x xs] - (concat (return x) xs)) - -(defn ^Observable concat - "Concatenate the given Observables one after the another. - - Note that xs is separate Observables which are concatentated. To concatenate an - Observable of Observables, use concat* - - See: - rx.Observable/concat - concat* - " - [& xs] - (Observable/concat (seq->o xs))) - -(defn ^Observable concat* - "Concatenate the given Observable of Observables one after another. - - See: - rx.Observable/concat - concat - " - [^Observable os] - (Observable/concat os)) - -(defn count - "Returns an Observable that emits the number of items is xs as a long. - - See: - rx.Observable/longCount - " - [^Observable xs] - (.longCount xs)) - -(defn cycle - "Returns an Observable that emits the items of xs repeatedly, forever. - - TODO: Other sigs. - - See: - rx.Observable/repeat - clojure.core/cycle - " - [^Observable xs] - (.repeat xs)) - -(defn distinct - "Returns an Observable of the elements of Observable xs with duplicates - removed. key-fn, if provided, is a one arg function that determines the - key used to determined duplicates. key-fn defaults to identity. - - This implementation doesn't use rx.Observable/distinct because it doesn't - honor Clojure's equality semantics. - - See: - clojure.core/distinct - " - ([xs] (distinct identity xs)) - ([key-fn ^Observable xs] - (let [op (operator* (fn [o] - (let [seen (atom #{})] - (subscriber o - (fn [o v] - (let [key (key-fn v)] - (when-not (contains? @seen key) - (swap! seen conj key) - (on-next o v))))))))] - (lift op xs)))) - -(defn ^Observable do - "Returns a new Observable that, for each x in Observable xs, executes (do-fn x), - presumably for its side effects, and then passes x along unchanged. - - If do-fn throws an exception, that exception is emitted via onError and the sequence - is finished. - - Example: - - (->> (rx/seq->o [1 2 3]) - (rx/do println) - ...) - - Will print 1, 2, 3. - - See: - rx.Observable/doOnNext - " - [do-fn ^Observable xs] - (.doOnNext xs (iop/action* do-fn))) - -(defn ^Observable drop - [n ^Observable xs] - (.skip xs n)) - -(defn ^Observable drop-while - [p ^Observable xs] - (.skipWhile xs (fn->predicate p))) - -(defn ^Observable every? - "Returns an Observable that emits a single true value if (p x) is true for - all x in xs. Otherwise emits false. - - See: - clojure.core/every? - rx.Observable/all - " - [p ^Observable xs] - (.all xs (fn->predicate p))) - -(defn ^Observable filter - [p ^Observable xs] - (.filter xs (fn->predicate p))) - -(defn ^Observable first - "Returns an Observable that emits the first item emitted by xs, or an - empty Observable if xs is empty. - - See: - rx.Observable/take(1) - " - [^Observable xs] - (.take xs 1)) - -(defn ^Observable group-by - "Returns an Observable of clojure.lang.MapEntry where the key is the result of - (key-fn x) and the val is an Observable of x for each key. - - This returns a clojure.lang.MapEntry rather than rx.observables.GroupedObservable - for some vague consistency with clojure.core/group-by and so that clojure.core/key, - clojure.core/val and destructuring will work as expected. - - See: - clojure.core/group-by - rx.Observable/groupBy - rx.observables.GroupedObservable - " - ([key-fn ^Observable xs] - (->> (.groupBy xs (iop/fn* key-fn)) - (map (fn [^GroupedObservable go] - (clojure.lang.MapEntry. (.getKey go) go)))))) - -(defn interleave* - "Returns an Observable of the first item in each Observable emitted by observables, then - the second etc. - - observables is an Observable of Observables - - See: - interleave - clojure.core/interleave - " - [observables] - (->> (map* #(seq->o %&) observables) - (concat*))) - -(defn interleave - "Returns an Observable of the first item in each Observable, then the second etc. - - Each argument is an individual Observable - - See: - observable* - clojure.core/interleave - " - [o1 & observables] - (->> (apply map #(seq->o %&) o1 observables) - (concat*))) - -(defn interpose - "Returns an Observable of the elements of xs separated by sep - - See: - clojure.core/interpose - " - [sep xs] - (let [op (operator* (fn [o] - (let [first? (atom true)] - (subscriber o (fn [o v] - (if-not (compare-and-set! first? true false) - (on-next o sep)) - (on-next o v))))))] - (lift op xs))) - -(defn into - "Returns an observable that emits a single value which is all of the - values of from-observable conjoined onto to - - See: - clojure.core/into - rx.Observable/toList - " - [to ^Observable from] - ; clojure.core/into uses transients if to is IEditableCollection - ; I don't think we have any guarantee that all on-next calls will be on the - ; same thread, so we can't do that here. - (reduce conj to from)) - -(defn iterate - "Returns an Observable of x, (f x), (f (f x)) etc. f must be free of side-effects - - See: - clojure.core/iterate - " - [f x] - (observable* (fn [s] - (loop [x x] - (when-not (unsubscribed? s) - (on-next s x) - (recur (f x))))))) - -(defn keep - [f xs] - (filter (complement nil?) (map f xs))) - -(defn keep-indexed - [f xs] - (filter (complement nil?) (map-indexed f xs))) - -(defn ^Observable map* - "Map a function over an Observable of Observables. - - Each item from the first emitted Observable is the first arg, each - item from the second emitted Observable is the second arg, and so on. - - See: - map - clojure.core/map - rx.Observable/zip - " - [f ^Observable observable] - (Observable/zip observable - ^rx.functions.FuncN (iop/fnN* f))) - -(defn ^Observable map - "Map a function over one or more observable sequences. - - Each item from the first Observable is the first arg, each item - from the second Observable is the second arg, and so on. - - See: - clojure.core/map - rx.Observable/zip - " - [f & observables] - (Observable/zip ^Iterable observables - ^rx.functions.FuncN (iop/fnN* f))) - -(defn ^Observable mapcat* - "Same as multi-arg mapcat, but input is an Observable of Observables. - - See: - mapcat - clojure.core/mapcat - " - [f ^Observable xs] - (->> xs - (map* f) - (concat*))) - -(defn ^Observable mapcat - "Returns an observable which, for each value x in xs, calls (f x), which must - return an Observable. The resulting observables are concatentated together - into one observable. - - If multiple Observables are given, the arguments to f are the first item from - each observable, then the second item, etc. - - See: - clojure.core/mapcat - rx.Observable/flatMap - " - [f & xs] - (if (clojure.core/next xs) - (mapcat* f (seq->o xs)) - ; use built-in flatMap for single-arg case - (.flatMap ^Observable (clojure.core/first xs) (iop/fn* f)))) - -(defn map-indexed - "Returns an observable that invokes (f index value) for each value of the input - observable. index starts at 0. - - See: - clojure.core/map-indexed - " - [f xs] - (let [op (operator* (fn [o] - (let [n (atom -1)] - (subscriber o - (fn [o v] - (catch-error-value o v - (on-next o (f (swap! n inc) v))))))))] - (lift op xs))) - -(def next - "Returns an observable that emits all but the first element of the input observable. - - See: - clojure.core/next - " - (partial drop 1)) - -(defn nth - "Returns an Observable that emits the value at the index in the given - Observable. nth throws an IndexOutOfBoundsException unless not-found - is supplied. - - Note that the Observable is the *first* arg! - " - ([^Observable xs index] - (.elementAt xs index)) - ([^Observable xs index not-found] - (.elementAtOrDefault xs index not-found))) - -(defn ^Observable partition-all - "Returns an Observable of Observables of n items each, at offsets step - apart. If step is not supplied, defaults to n, i.e. the partitions - do not overlap. May include partitions with fewer than n items at the end. - - See: - clojure.core/partition-all - rx.Observable/window - " - ([n ^Observable xs] (.window xs (int n))) - ([n step ^Observable xs] (.window xs (int n) (int step)))) - -(defn range - "Returns an Observable nums from start (inclusive) to end - (exclusive), by step, where start defaults to 0, step to 1, and end - to infinity. - - Note: this is not implemented on rx.Observable/range - - See: - clojure.core/range - " - ([] (range 0 Double/POSITIVE_INFINITY 1)) - ([end] (range 0 end 1)) - ([start end] (range start end 1)) - ([start end step] - (observable* (fn [s] - (let [comp (if (pos? step) < >)] - (loop [i start] - (if-not (unsubscribed? s) - (if (comp i end) - (do - (on-next s i) - (recur (+ i step))) - (on-completed s))))))))) - -(defn ^Observable reduce - ([f ^Observable xs] (.reduce xs (iop/fn* f))) - ([f val ^Observable xs] (.reduce xs val (iop/fn* f)))) - -(defn ^Observable reductions - ([f ^Observable xs] (.scan xs (iop/fn* f))) - ([f val ^Observable xs] (.scan xs val (iop/fn* f)))) - -(def rest - "Same as rx/next" - next) - -(defn some - "Returns an observable that emits the first logical true value of (pred x) for - any x in xs, else completes immediately. - - See: - clojure.core/some - " - [p ^Observable xs] - (->> xs - (map p) - (filter identity) - first)) - -(defn ^:private sorted-list-by - ([keyfn coll] (sorted-list-by keyfn clojure.core/compare coll)) - ([keyfn comp ^Observable coll] - (.toSortedList coll (iop/fn [a b] - ; force to int so rxjava doesn't have a fit - (int (comp (keyfn a) (keyfn b))))))) - -(defn sort - "Returns an observable that emits the items in xs, where the sort order is - determined by comparing items. If no comparator is supplied, uses compare. - comparator must implement java.util.Comparator. - - See: - clojure.core/sort - " - ([xs] - (sort clojure.core/compare xs)) - ([comp xs] - (->> xs - (sorted-list-by identity comp) - (mapcat seq->o)))) - -(defn sort-by - "Returns an observable that emits the items in xs, where the sort order is - determined by comparing (keyfn item). If no comparator is supplied, uses - compare. comparator must implement java.util.Comparator. - - See: - clojure.core/sort-by - " - ([keyfn xs] - (sort-by keyfn clojure.core/compare xs)) - ([keyfn comp ^Observable xs] - (->> xs - (sorted-list-by keyfn comp) - (mapcat seq->o)))) - -(defn split-with - "Returns an observable that emits a pair of observables - - [(take-while p xs) (drop-while p xs)] - - See: - rx.lang.clojure/take-while - rx.lang.clojure/drop-while - clojure.core/split-with - " - [p xs] - (return [(take-while p xs) (drop-while p xs)])) - -(defn ^Observable take - "Returns an observable that emits the first n elements of xs. - - See: - clojure.core/take - " - [n ^Observable xs] - {:pre [(>= n 0)]} - (.take xs n)) - -(defn take-while - "Returns an Observable that emits xs until the first x such that - (p x) is falsey. - - See: - clojure.core/take-while - rx.Observable/takeWhile - " - [p ^Observable xs] - (.takeWhile xs (fn->predicate p))) - -;################################################################################; - -(defn throw - "Returns an Observable the simply emits the given exception with on-error - - See: - rx.Observable/error - " - [^Throwable e] - (Observable/error e)) - -(defn catch* - "Returns an observable that, when Observable o triggers an error, e, continues with - Observable returned by (f e) if (p e) is true. If (p e) returns a Throwable - that value is passed as e. - - If p is a class object, a normal instance? check is performed rather than calling it - as a function. If the value returned by (p e) is not true, the error is propagated. - - Examples: - - (->> my-observable - - ; On IllegalArgumentException, just emit 1 - (catch* IllegalArgumentException - (fn [e] (rx/return 1))) - - ; If exception message contains \"WAT\", emit [\\W \\A \\T] - (catch* (fn [e] (-> e .getMessage (.contains \"WAT\"))) - (fn [e] (rx/seq->o [\\W \\A \\T])))) - - See: - rx.Observable/onErrorResumeNext - http://netflix.github.io/RxJava/javadoc/rx/Observable.html#onErrorResumeNext(rx.functions.Func1) - " - [p f ^Observable o] - (let [p (if (class? p) - (fn [e] (.isInstance ^Class p e)) - p)] - (.onErrorResumeNext o - ^Func1 (iop/fn [e] - (if-let [maybe-e (p e)] - (f (if (instance? Throwable maybe-e) - maybe-e - e)) - (rx.lang.clojure.core/throw e)))))) - -(defmacro catch - "Macro version of catch*. - - The body of the catch is wrapped in an implicit (do). It must evaluate to an Observable. - - Note that the source observable is the last argument so this works with ->> but may look - slightly odd when used standalone. - - Example: - - (->> my-observable - ; just emit 0 on IllegalArgumentException - (catch IllegalArgumentException e - (rx/return 0)) - - (catch DependencyException e - (if (.isMinor e) - (rx/return 0) - (rx/throw (WebException. 503))))) - - See: - catch* - " - {:arglists '([p binding & body observable])} - [p binding & body] - (let [o (last body) - body (butlast body)] - `(catch* ~p - (fn [~binding] ~@body) - ~o))) - -(defn finally* - "Returns an Observable that, as a side-effect, executes (f) when the given - Observable completes regardless of success or failure. - - Example: - - (->> my-observable - (finally* (fn [] (println \"Done\")))) - - " - [f ^Observable o] - (.finallyDo o ^Action0 (iop/action* f))) - -(defmacro finally - "Macro version of finally*. - - Note that the source observable is the last argument so this works with ->> but may look - slightly odd when used standalone. - - Example: - - (->> my-observable - (finally (println \"Done\"))) - - See: - finally* - " - {:arglists '([& body observable])} - [& body] - (let [o (last body) - body (butlast body)] - `(finally* (fn [] ~@body) ~o))) - -;################################################################################; - -(defn generator* - "Creates an observable that calls (f observer & args) which should emit values - with (rx/on-next observer value). - - Automatically calls on-completed on return, or on-error if any exception is thrown. - - f should exit early if (rx/unsubscribed? observable) returns true - - Examples: - - ; An observable that emits just 99 - (rx/generator* on-next 99) - " - [f & args] - (observable* (-> #(apply f % args) - wrap-on-completed - wrap-on-error))) - -(defmacro generator - "Create an observable that executes body which should emit values with - (rx/on-next observer value) where observer comes from bindings. - - Automatically calls on-completed on return, or on-error if any exception is thrown. - - The body should exit early if (rx/unsubscribed? observable) returns true - - Examples: - - ; make an observer that emits [0 1 2 3 4] - (generator [observer] - (dotimes [i 5] - (on-next observer i))) - - " - [bindings & body] - `(generator* (fn ~bindings ~@body))) - -;################################################################################; - -; Import public graph symbols here. I want them in this namespace, but implementing -; them here with all the clojure.core symbols excluded is a pain. -(intern *ns* (with-meta 'let-o* (meta #'graph/let-o*)) @#'graph/let-o*) -(intern *ns* (with-meta 'let-o (meta #'graph/let-o)) @#'graph/let-o) - -;################################################################################; - -; Import some public realized symbols here. I want them in this namespace, but implementing -; them here with all the clojure.core symbols excluded is a pain. -(intern *ns* (with-meta 'let-realized (meta #'realized/let-realized)) @#'realized/let-realized) - diff --git a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/future.clj b/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/future.clj deleted file mode 100644 index 83b56b27b6..0000000000 --- a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/future.clj +++ /dev/null @@ -1,71 +0,0 @@ -(ns rx.lang.clojure.future - "Functions and macros for making rx-ified futures. That is, run some code in some - other thread and return an Observable of its result. - " - (:require [rx.lang.clojure.interop :as iop] - [rx.lang.clojure.core :as rx])) - -(def ^:private -ns- *ns*) -(set! *warn-on-reflection* true) - -(defn future* - "Exerimental/Possibly a bad idea - - Execute (f & args) in a separate thread and pass the result to onNext. - If an exception is thrown, onError is called with the exception. - - runner is a function that takes a no-arg function argument and returns a future - representing the execution of that function. - - Returns an Observable. If the subscriber unsubscribes, the future will be canceled - with clojure.core/future-cancel - - Examples: - - (subscribe (rx/future future-call - #(slurp \"input.txt\")) - (fn [v] (println \"Got: \" v))) - ; eventually outputs content of input.txt - " - [runner f & args] - {:pre [(ifn? runner) (ifn? f)]} - (rx/observable* (fn [^rx.Subscriber observer] - (let [wrapped (-> #(rx/on-next % (apply f args)) - rx/wrap-on-completed - rx/wrap-on-error) - fu (runner #(wrapped observer))] - (.add observer - (rx/subscription #(future-cancel fu))))))) - -(defn future-generator* - "Exerimental/Possibly a bad idea - - Same as rx/generator* except f is invoked in a separate thread. - - runner is a function that takes a no-arg function argument and returns a future - representing the execution of that function. - - Returns an Observable. If the subscriber unsubscribes, the future will be canceled - with clojure.core/future-cancel - - Example: - - (future-generator* future-call - (fn [o] - (rx/on-next o 1) - (Thread/sleep 1000) - (rx/on-next o 2))) - - See: - rx.lang.clojure.core/generator* - " - [runner f & args] - {:pre [(ifn? runner) (ifn? f)]} - (rx/observable* (fn [^rx.Subscriber observer] - (let [wrapped (-> (fn [o] - (apply f o args)) - rx/wrap-on-completed - rx/wrap-on-error) - fu (runner #(wrapped observer))] - (.add observer - (rx/subscription #(future-cancel fu))))))) diff --git a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/graph.clj b/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/graph.clj deleted file mode 100644 index a67ebba47c..0000000000 --- a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/graph.clj +++ /dev/null @@ -1,141 +0,0 @@ -(ns rx.lang.clojure.graph - "This is an implementation namespace. Don't use it directly. Use the symbols - in rx.lang.clojure.core - " - (:require [clojure.set :as set])) - -(def ^:private -ns- *ns*) -(set! *warn-on-reflection* true) - -(defn ^:private ->let-o*-observable - [^rx.Observable o n name] - (if (= n 1) - o - ; TODO This is a shortcut. We know the expected number of subscriptions so - ; we only need to cache values until we get the nth subscription at which - ; point, it just becomes a pass through. I haven't found a cache/replay-ish - ; operator that gives this level of control over the cached values - (.cache o))) - -(defn let-o* - "EXTREMELY EXPERIMENTAL AND SUBJECT TO CHANGE OR DELETION - - Given a graph description, returns an observable that emits a single - map of observables all hooked up and ready for subscription. - - A graph is a map from name to a map with keys: - - :deps A vector of dependency names - :factory A function that takes a map from name to Observable - for the names in :deps and returns an Observable - - Returns a map from name to Observable. Additionally, there will be a - ::non-terminals key in the map with a vector of non-terminal names. - - See: - let-o - " - [description] - (let [in-dep-counts (->> description - vals - (mapcat :deps) - frequencies) - terminals (set/difference (set (keys description)) (set (keys in-dep-counts))) - non-terminals (vec (keys in-dep-counts)) - - resolve-node (fn resolve-node [state {:keys [id deps factory] :as node}] - (let [existing (state id)] - (cond - ; It's already resolving up the stack. We've hit a cycle. - (= ::resolving existing) (throw (IllegalArgumentException. (format "Cycle found at '%s'" id))) - - ; It's already resolved. Done. - (not (nil? existing)) state - - :else - ; recursively resolve dependencies - (let [new-state (reduce (fn [s dep] - (if-let [dep-node (description dep)] - (resolve-node s (assoc dep-node :id dep)) - (throw (IllegalArgumentException. (format "Unknown node '%s' referenced from '%s'" dep id))))) - (assoc state id ::resolving) - deps) - ; execute the factory function and wrap it in an observable that delays dependencies - o (-> (select-keys new-state deps) - factory - (->let-o*-observable (in-dep-counts id 1) id))] - ; return the updated state with the resolved node - (assoc new-state id o)))))] - ; resolve the graph and build the result map - (-> (reduce (fn [s [id node]] - (resolve-node s (assoc node :id id))) - {} - description) - (select-keys terminals) - (assoc ::non-terminals non-terminals)))) - -(defmacro let-o - "EXTREMELY EXPERIMENTAL AND SUBJECT TO CHANGE OR DELETION - - Similar to clojure.core/let, but bindings are Observables and the result of the body - must be an Observable. Binding names must start with ?. Binding order doesn't matter - and any binding is visible to all other expressions as long as no cycles are produced - in the resulting Observable expression. - - The key difference here is that the macro can identify the dependencies between Observables - and correctly connect them, protecting from variations in subscribe behavior as well as - the idiosyncracies of setting up multiple subscriptions to Observables. - - This is only very useful for constructing graphs of Observables where you'd usually have - to fiddle around with publish, connect, replay and all that stuff. If you have a linear - sequence of operators, just chain them together. - - Current limitations: - - * All Observables are cache()'d so watch out for large sequences. This will be - fixed eventually. - * let-o cannot be nested. Some deep-walking macro-magic will be required for this. - - Example: - - ; Note that both ?c and ?d depend on ?b and the result Observable depends on - ; ?c and ?d. - (let-o [?a (rx/return 99) - ?b (... some observable network request ...) - ?c (rx/map vector ?a ?b) - ?d (rx/map ... ?b)] - (rx/map vector ?c ?d)) - - See: - let-o* - " - [bindings & result-body] - (let [sym->dep-sym (fn [s] - (when (and (symbol? s) - (not (namespace s)) - (.startsWith (name s) "?")) - s)) - body->dep-syms (fn [body] - (->> body - flatten - (keep sym->dep-sym) - distinct - vec)) - ->node-map (fn [[id & body]] - (let [dep-syms (body->dep-syms body) - dep-keys (->> dep-syms (map (comp keyword name)) vec)] - [(keyword (name id)) {:deps dep-keys - :factory `(fn [{:keys ~dep-syms}] ~@body) }])) - node-map (let [base-map (->> bindings - (partition 2) - (map ->node-map) - (into {})) - result-dep-syms (body->dep-syms result-body)] - (assoc base-map - :rx.lang.clojure.core/result - {:deps (mapv keyword result-dep-syms) - :factory `(fn [{:keys ~result-dep-syms}] ~@result-body) }))] - `(->> ~node-map - let-o* - :rx.lang.clojure.core/result))) - 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 deleted file mode 100644 index 5f8b803980..0000000000 --- a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/interop.clj +++ /dev/null @@ -1,131 +0,0 @@ -(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 - ; If they want Func1, give them onSubscribe as well so Observable/create can be - ; used seemlessly with rx/fn. - ; TODO remove this when OnSubscriberFunc is removed - ~@(if (and (= prefix "rx.functions.Func") - (some #{1} arities)) - `(rx.Observable$OnSubscribeFunc - (~'onSubscribe [~'this observer#] - (~f-name observer#)))) - - ; OnSubscribe is just an Action1, so add it to the list of implemented interfaces - ; so an action cab be used with Observable/create - ~@(if (and (= prefix "rx.functions.Action") - (some #{1} arities)) - `(rx.Observable$OnSubscribe)) - - ~@(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.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. - - This will also implement rx.Observable$OnSubscribeFunc.onSubscribe for use with - Observable/create. In this case, the function must take an Observable as its single - argument and return a subscription object. - - Example: - - (.reduce my-numbers (rx/fn* +)) - - See: - http://netflix.github.io/RxJava/javadoc/rx/functions/Func0.html - " - [f] - (reify-callable "rx.functions.Func" [0 1 2 3 4 5 6 7 8 9] f)) - -(defn fnN* - "Given function f, returns an object that implements rx.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/functions/FuncN.html - " - [f] - (reify rx.functions.FuncN - (call [this objects] - (apply f objects)))) - -(defmacro fn - "Like clojure.core/fn, but returns the appropriate rx.functions.Func* - interface. - - Example: - - (.map my-observable (rx/fn [a] (* 2 a))) - - or, to create an Observable: - - (Observable/create (rx/fn [observer] - (.onNext observer 10) - (.onCompleted observer) - (Subscriptions/empty))) - - See: - rx.lang.clojure.interop/fn* - " - [& fn-form] - ; preserve metadata so type hints work - ; have to qualify fn*. Otherwise bad things happen with the fn* special form in clojure - (with-meta `(rx.lang.clojure.interop/fn* (clojure.core/fn ~@fn-form)) - (meta &form))) - -(defn action* - "Given function f, returns an object that implements rx.functions.Action0-3 - by delegating to the given function. Also implements rx.Observable$OnSubscribe which - is just an Action1. - - Example: - - (.subscribe my-observable (rx/action* println)) - - See: - http://netflix.github.io/RxJava/javadoc/rx/functions/Action0.html - " - [f] - (reify-callable "rx.functions.Action" [0 1 2 3] f)) - -(defmacro action - "Like clojure.core/fn, but returns the appropriate rx.functions.Action* - interface. - - Example: - - (.finallyDo my-observable (rx/action [] (println \"Finally!\"))) - - " - [& fn-form] - ; preserve metadata so type hints work - (with-meta `(action* (clojure.core/fn ~@fn-form)) - (meta &form))) - -;################################################################################ diff --git a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/realized.clj b/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/realized.clj deleted file mode 100644 index 2926633924..0000000000 --- a/language-adaptors/rxjava-clojure/src/main/clojure/rx/lang/clojure/realized.clj +++ /dev/null @@ -1,134 +0,0 @@ -(ns rx.lang.clojure.realized - (:require [rx.lang.clojure.interop :as iop])) - -(def ^:private -ns- *ns*) -(set! *warn-on-reflection* true) - -(defrecord ^:private PostProc [o f]) - -(defn all - "Tell realized map to capture all output of the observable, not just the last one" - [o] - (->PostProc o identity)) - -(defn only - "Tell realized map to capture the only value emitted by the observable. - If there are 0 or more than one values, an IllegalStateException is thrown - which should propagate to onError. - - This is the default mode of realized-map and let-realized. - " - [o] - (->PostProc o (fn [values] - (condp = (count values) - 1 (first values) - (throw (IllegalStateException. "Observable did not produce exactly one value")))))) - -(defn ^:private ->post-proc - [v] - (cond - (instance? rx.Observable v) (only v) - (instance? PostProc v) v - (vector? v) (->PostProc (first v) - (apply comp (reverse (next v)))) - :else (->post-proc (rx.Observable/just v)))) - -(defn realized-map - "EXTREMELY EXPERIMENTAL AND SUBJECT TO CHANGE OR DELETION - - See let-realized. - - Given a map from key to observable, returns an observable that emits a single - map from the same keys to the values emitted by their corresponding observable. - - keyvals is a list of key/value pairs where key is a key in the emitted map and val - can be one of the following: - - rx.Observable The only value of the emitted sequence is bound to the key. This is the - default since this is often a singleton response from a request. If the - Observable produces 0 or more than 1 values, an IllegalStateException is - produced. - - vector The first element of the vector must be an Observable. Remaining elements - are functions applied in sequence to the list of values emitted by the - observable. For example [my-observable first] will result in a single - value in the emitted map rather than a vector of values. - - other The value is placed in the emitted map as is - - Note the observable can also be wrapped with realized/all to get the full list rather than - just the last value. - - The purpose of this is to simplify the messy pattern of mapping observables to - single key maps, merging and then folding all the separate maps together. So code - like this: - - ; TODO update - (->> (rx/merge (->> (user-info-o user-id) - (rx/map (fn [u] {:user u}))) - (->> (user-likes-o user-id) - (rx/map (fn [u] {:likes u})))) - (rx/reduce merge {})) - - becomes: - - (realized-map :user (user-info-o user-id) - :likes (user-likes-o user-id)) - - See: - let-realized - " - [& keyvals] - (let [o (->> keyvals - (partition 2) - ; generate a sequence of observables - (map (fn [[k v]] - (let [{:keys [^rx.Observable o f]} (->post-proc v)] - ; pour the observable into a single list and apply post-proc func to it - (-> o - .toList - (.map (iop/fn [list] {k (f list)})))))))] - - (-> ^Iterable o - (rx.Observable/merge) ; funnel all the observables into a single sequence - (.reduce {} (iop/fn* merge))))) ; do the map merge dance - -(defn ^rx.Observable realized-map* - "EXTREMELY EXPERIMENTAL AND SUBJECT TO CHANGE OR DELETION - - Same as realized-map, but takes a map argument rather than key-value pairs." - [map-description] - (apply realized-map (apply concat map-description))) - -(defmacro let-realized - "EXTREMELY EXPERIMENTAL AND SUBJECT TO CHANGE OR DELETION - - 'let' version of realized map. - - (let-realized [a (make-observable)] - (* 2 a)) - - is equivalent to: - - (->> (realized-map :a (make-observable)) - (map (fn [{:keys [a]}] (* 2 a)))) - - That is, it eliminates the repition of the map keys when you want to do something - with the final result. - - Evaluates to an Observable that emits the value of the let body. - - See: - rx.lang.clojure.realized/realized-map - rx.lang.clojure.realized/all - " - [bindings & body] - (let [b-parts (partition 2 bindings) - b-map (->> b-parts - (map (fn [[k v]] - [(keyword (name k)) v])) - (into {})) - b-names (mapv first b-parts)] - `(.map (realized-map* ~b-map) - (iop/fn [{:keys ~b-names}] ~@body)))) - diff --git a/language-adaptors/rxjava-clojure/src/main/java/rx/lang/clojure/interop/DummyObservable.java b/language-adaptors/rxjava-clojure/src/main/java/rx/lang/clojure/interop/DummyObservable.java deleted file mode 100644 index 805198b6fa..0000000000 --- a/language-adaptors/rxjava-clojure/src/main/java/rx/lang/clojure/interop/DummyObservable.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.clojure.interop; - -// Dummy class with some overloads to make sure that type hinting works -// correctly with the fn and action macros. Used only for testing. -public class DummyObservable { - - public String call(Object f) { - return "Object"; - } - - public String call(rx.functions.Func1 f) { - return "rx.functions.Func1"; - } - - public String call(rx.functions.Func2 f) { - return "rx.functions.Func2"; - } - - public String call(rx.functions.Action1 f) { - return "rx.functions.Action1"; - } - - public String call(rx.functions.Action2 f) { - return "rx.functions.Action2"; - } - -} diff --git a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/blocking_test.clj b/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/blocking_test.clj deleted file mode 100644 index 260e6a3dc5..0000000000 --- a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/blocking_test.clj +++ /dev/null @@ -1,65 +0,0 @@ -(ns rx.lang.clojure.blocking-test - (:require [rx.lang.clojure.blocking :as b] - [rx.lang.clojure.core :as rx] - [clojure.test :refer [deftest testing is]])) - -(deftest test-->blocking - (testing "returns a BlockingObservable from an Observable" - (is (instance? rx.observables.BlockingObservable (b/->blocking (rx/return 0))))) - - (testing "is idempotent" - (is (instance? rx.observables.BlockingObservable (b/->blocking (b/->blocking (rx/return 0))))))) - - -(deftest test-o->seq - (is (= [1 2 3] (b/o->seq (rx/seq->o [1 2 3]))))) - -(deftest test-first - (testing "returns first element of observable" - (is (= 1 (b/first (rx/seq->o [1 2 3]))))) - (testing "returns nil for empty observable" - (is (nil? (b/first (rx/empty))))) - (testing "rethrows errors" - (is (thrown? java.io.FileNotFoundException - (b/first (rx/throw (java.io.FileNotFoundException. "boo"))))))) - -(deftest test-last - (testing "returns last element of observable" - (is (= 3 (b/last (rx/seq->o [1 2 3]))))) - (testing "returns nil for empty observable" - (is (nil? (b/last (rx/empty))))) - (testing "rethrows errors" - (is (thrown? java.io.FileNotFoundException - (b/last (rx/throw (java.io.FileNotFoundException. "boo"))))))) - -(deftest test-single - (testing "returns one element" - (is (= 1 (b/single (rx/return 1))))) - (testing "throw if empty" - (is (thrown? java.util.NoSuchElementException (b/single (rx/empty))))) - (testing "throw if many" - (is (thrown? java.lang.IllegalArgumentException (b/single (rx/seq->o [1 2]))))) - (testing "rethrows errors" - (is (thrown? java.io.FileNotFoundException - (b/single (rx/throw (java.io.FileNotFoundException. "boo"))))))) - -(deftest test-into - (is (= [1 2 3] - (b/into [1] (rx/seq->o [2 3])))) - (testing "rethrows errors" - (is (thrown? java.io.FileNotFoundException - (b/into #{} (rx/throw (java.io.FileNotFoundException. "boo"))))))) - -(deftest test-doseq - (is (= (range 3) - (let [capture (atom [])] - (b/doseq [{:keys [value]} (rx/seq->o (map #(hash-map :value %) (range 3)))] - (println value) - (swap! capture conj value)) - @capture))) - - (testing "rethrows errors" - (is (thrown? java.io.FileNotFoundException - (b/doseq [i (rx/seq->o (range 3))] - (throw (java.io.FileNotFoundException. "boo"))))))) - diff --git a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/chunk_test.clj b/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/chunk_test.clj deleted file mode 100644 index 58ef044c9d..0000000000 --- a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/chunk_test.clj +++ /dev/null @@ -1,65 +0,0 @@ -(ns rx.lang.clojure.chunk-test - (:require [rx.lang.clojure.chunk :as rx-chunk] - [rx.lang.clojure.core :as rx] - [rx.lang.clojure.future :as rx-future] - [rx.lang.clojure.blocking :as rx-blocking] - [clojure.test :refer [deftest testing is]])) - - -(deftest test-chunk - (let [n 20 - chunk-size 10 - factory (rx-future/future-generator* - future-call - (fn[o] - (doseq [i (range n)] - (Thread/sleep (rand-int 50)) - (rx/on-next o (rx-future/future* - future-call - #(let [t (rand-int 500)] - (Thread/sleep t) - i))))))] - (is (= (range n) - (sort (rx-blocking/into [] - (rx-chunk/chunk chunk-size {:debug true} factory))))))) - -(deftest test-chunk-with-error - (testing "error from source is propagated" - (let [n 20 - chunk-size 4 - factory (rx-future/future-generator* - future-call - (fn [o] - (doseq [i (range n)] - (Thread/sleep (rand-int 50)) - (rx/on-next o (rx-future/future* - future-call - #(let [t (rand-int 1000)] - (Thread/sleep t) - i)))) - (throw (IllegalArgumentException. "hi"))))] - (is (thrown-with-msg? IllegalArgumentException #"hi" - (rx-blocking/into [] - (rx-chunk/chunk chunk-size {:debug true} factory)))))) - - (testing "error from single observable is propagated" - (let [n 20 - chunk-size 4 - factory (rx-future/future-generator* - future-call - (fn [o] - (doseq [i (range n)] - (Thread/sleep (rand-int 50)) - (rx/on-next o (rx-future/future* - future-call - #(let [t (rand-int 1000)] - (throw (IllegalArgumentException. "byebye")) - (Thread/sleep t) - i))))))] - (is (thrown? rx.exceptions.CompositeException - (rx-blocking/into [] - (rx-chunk/chunk chunk-size - {:debug true - :delay-error? true } - factory))))))) - diff --git a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/core_test.clj b/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/core_test.clj deleted file mode 100644 index a4d842e32c..0000000000 --- a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/core_test.clj +++ /dev/null @@ -1,675 +0,0 @@ -(ns rx.lang.clojure.core-test - (:require [rx.lang.clojure.core :as rx] - [rx.lang.clojure.blocking :as b] - [rx.lang.clojure.future :as f] - [clojure.test :refer [deftest is testing are]])) - -(deftest test-observable? - (is (rx/observable? (rx/return 99))) - (is (not (rx/observable? "I'm not an observable")))) - -(deftest test-on-next - (testing "calls onNext" - (let [called (atom []) - o (reify rx.Observer (onNext [this value] (swap! called conj value)))] - (is (identical? o (rx/on-next o 1))) - (is (= [1] @called))))) - -(deftest test-on-completed - (testing "calls onCompleted" - (let [called (atom 0) - o (reify rx.Observer (onCompleted [this] (swap! called inc)))] - (is (identical? o (rx/on-completed o))) - (is (= 1 @called))))) - -(deftest test-on-error - (testing "calls onError" - (let [called (atom []) - e (java.io.FileNotFoundException. "yum") - o (reify rx.Observer (onError [this e] (swap! called conj e)))] - (is (identical? o (rx/on-error o e))) - (is (= [e] @called))))) - -(deftest test-catch-error-value - (testing "if no exception, returns body" - (let [o (reify rx.Observer)] - (is (= 3 (rx/catch-error-value o 99 - (+ 1 2)))))) - - (testing "exceptions call onError on observable and inject value in exception" - (let [called (atom []) - e (java.io.FileNotFoundException. "boo") - o (reify rx.Observer - (onError [this e] - (swap! called conj e))) - result (rx/catch-error-value o 100 - (throw e)) - cause (.getCause e)] - (is (identical? e result)) - (is (= [e] @called)) - (when (is (instance? rx.exceptions.OnErrorThrowable$OnNextValue cause)) - (is (= 100 (.getValue cause))))))) - -(deftest test-subscribe - (testing "subscribe overload with only onNext" - (let [o (rx/return 1) - called (atom nil)] - (rx/subscribe o (fn [v] (swap! called (fn [_] v)))) - (is (= 1 @called))))) - -(deftest test-fn->predicate - (are [f arg result] (= result (.call (rx/fn->predicate f) arg)) - identity nil false - identity false false - identity 1 true - identity "true" true - identity true true)) - -(deftest test-subscription - (let [called (atom 0) - s (rx/subscription #(swap! called inc))] - (is (identical? s (rx/unsubscribe s))) - (is (= 1 @called)))) - -(deftest test-unsubscribed? - (let [s (rx/subscription #())] - (is (not (rx/unsubscribed? s))) - (rx/unsubscribe s) - (is (rx/unsubscribed? s)))) - - -(deftest test-observable* - (let [o (rx/observable* (fn [s] - (rx/on-next s 0) - (rx/on-next s 1) - (when-not (rx/unsubscribed? s) (rx/on-next s 2)) - (rx/on-completed s)))] - (is (= [0 1 2] (b/into [] o))))) - -(deftest test-operator* - (let [o (rx/operator* #(rx/subscriber % - (fn [o v] - (if (even? v) - (rx/on-next o v))))) - result (->> (rx/seq->o [1 2 3 4 5]) - (rx/lift o) - (b/into []))] - (is (= [2 4] result)))) - -(deftest test-serialize - ; I'm going to believe serialize works and just exercise it - ; here for sanity. - (is (= [1 2 3] - (->> [1 2 3] - (rx/seq->o) - (rx/serialize) - (b/into []))))) - -(let [expected-result [[1 3 5] [2 4 6]] - sleepy-o #(f/future-generator* - future-call - (fn [o] - (doseq [x %] - (Thread/sleep 10) - (rx/on-next o x)))) - make-inputs (fn [] (mapv sleepy-o expected-result)) - make-output (fn [r] [(keep #{1 3 5} r) - (keep #{2 4 6} r)])] - (deftest test-merge* - (is (= expected-result - (->> (make-inputs) - (rx/seq->o) - (rx/merge*) - (b/into []) - (make-output))))) - (deftest test-merge - (is (= expected-result - (->> (make-inputs) - (apply rx/merge) - (b/into []) - (make-output))))) - (deftest test-merge-delay-error* - (is (= expected-result - (->> (make-inputs) - (rx/seq->o) - (rx/merge-delay-error*) - (b/into []) - (make-output))))) - (deftest test-merge-delay-error - (is (= expected-result - (->> (make-inputs) - (apply rx/merge-delay-error) - (b/into []) - (make-output)))))) - -(deftest test-generator - (testing "calls on-completed automatically" - (let [o (rx/generator [o]) - called (atom nil)] - (rx/subscribe o (fn [v]) (fn [_]) #(reset! called "YES")) - (is (= "YES" @called)))) - - (testing "exceptions automatically go to on-error" - (let [expected (IllegalArgumentException. "hi") - actual (atom nil)] - (rx/subscribe (rx/generator [o] (throw expected)) - #() - #(reset! actual %)) - (is (identical? expected @actual))))) - -(deftest test-seq->o - (is (= [] (b/into [] (rx/seq->o [])))) - (is (= [] (b/into [] (rx/seq->o nil)))) - (is (= [\a \b \c] (b/into [] (rx/seq->o "abc")))) - (is (= [0 1 2 3] (b/first (rx/into [] (rx/seq->o (range 4)))))) - (is (= #{0 1 2 3} (b/first (rx/into #{} (rx/seq->o (range 4)))))) - (is (= {:a 1 :b 2 :c 3} (b/first (rx/into {} (rx/seq->o [[:a 1] [:b 2] [:c 3]])))))) - -(deftest test-return - (is (= [0] (b/into [] (rx/return 0))))) - -(deftest test-cache - (let [value (atom 0) - o (->> - (rx/return 0) - (rx/map (fn [x] (swap! value inc))) - (rx/cache))] - (is (= 1 (b/single o))) - (is (= 1 @value)) - (is (= 1 (b/single o))) - (is (= 1 @value)) - (is (= 1 (b/single o))))) - -(deftest test-cons - (is (= [1] (b/into [] (rx/cons 1 (rx/empty))))) - (is (= [1 2 3 4] (b/into [] (rx/cons 1 (rx/seq->o [2 3 4])))))) - -(deftest test-concat - (is (= [:q :r] - (b/into [] (rx/concat (rx/seq->o [:q :r]))))) - (is (= [:q :r 1 2 3] - (b/into [] (rx/concat (rx/seq->o [:q :r]) - (rx/seq->o [1 2 3])))))) - -(deftest test-concat* - (is (= [:q :r] - (b/into [] (rx/concat* (rx/return (rx/seq->o [:q :r])))))) - (is (= [:q :r 1 2 3] - (b/into [] (rx/concat* (rx/seq->o [(rx/seq->o [:q :r]) - (rx/seq->o [1 2 3])])))))) - -(deftest test-count - (are [xs] (= (count xs) (->> xs (rx/seq->o) (rx/count) (b/single))) - [] - [1] - [5 6 7] - (range 10000))) - -(deftest test-cycle - (is (= [1 2 3 1 2 3 1 2 3 1 2] - (->> [1 2 3] - (rx/seq->o) - (rx/cycle) - (rx/take 11) - (b/into []))))) - -(deftest test-distinct - (let [input [{:a 1} {:a 1} {:b 1} {"a" (int 1)} {:a (int 1)}]] - (is (= (distinct input) - (->> input - (rx/seq->o) - (rx/distinct) - (b/into []))))) - (let [input [{:name "Bob" :x 2} {:name "Jim" :x 99} {:name "Bob" :x 3}]] - (is (= [{:name "Bob" :x 2} {:name "Jim" :x 99}] - (->> input - (rx/seq->o) - (rx/distinct :name) - (b/into [])))))) - -(deftest test-do - (testing "calls a function with each element" - (let [collected (atom [])] - (is (= [1 2 3] - (->> (rx/seq->o [1 2 3]) - (rx/do (fn [v] - (swap! collected conj (* 2 v)))) - (rx/do (partial println "GOT")) - (b/into [])))) - (is (= [2 4 6] @collected)))) - (testing "ends sequence with onError if action code throws an exception" - (let [collected (atom []) - o (->> (rx/seq->o [1 2 3]) - (rx/do (fn [v] - (if (= v 2) - (throw (IllegalStateException. (str "blah" v))) - (swap! collected conj (* 99 v))))))] - (is (thrown-with-msg? IllegalStateException #"blah2" - (b/into [] o))) - (is (= [99] @collected))))) - -(deftest test-drop-while - (is (= (into [] (drop-while even? [2 4 6 8 1 2 3])) - (b/into [] (rx/drop-while even? (rx/seq->o [2 4 6 8 1 2 3]))))) - (is (= (into [] (drop-while even? [2 4 6 8 1 2 3])) - (b/into [] (rx/drop-while even? (rx/seq->o [2 4 6 8 1 2 3])))))) - -(deftest test-every? - (are [xs p result] (= result (->> xs (rx/seq->o) (rx/every? p) (b/single))) - [2 4 6 8] even? true - [2 4 3 8] even? false - [1 2 3 4] #{1 2 3 4} true - [1 2 3 4] #{1 3 4} false)) - -(deftest test-filter - (is (= (into [] (->> [:a :b :c :d :e :f :G :e] - (filter #{:b :e :G}))) - (b/into [] (->> (rx/seq->o [:a :b :c :d :e :f :G :e]) - (rx/filter #{:b :e :G})))))) - -(deftest test-first - (is (= [3] - (b/into [] (rx/first (rx/seq->o [3 4 5]))))) - (is (= [] - (b/into [] (rx/first (rx/empty)))))) - -(deftest test-group-by - (let [xs [{:k :a :v 1} {:k :b :v 2} {:k :a :v 3} {:k :c :v 4}]] - (testing "with just a key-fn" - (is (= [[:a {:k :a :v 1}] - [:b {:k :b :v 2}] - [:a {:k :a :v 3}] - [:c {:k :c :v 4}]] - (->> xs - (rx/seq->o) - (rx/group-by :k) - (rx/mapcat (fn [[k vo :as me]] - (is (instance? clojure.lang.MapEntry me)) - (rx/map #(vector k %) vo))) - (b/into []))))) - - ; TODO reinstate once this is implemented - ; see https://github.com/Netflix/RxJava/commit/02ccc4d727a9297f14219549208757c6e0efce2a - #_(testing "with a val-fn" - (is (= [[:a 1] - [:b 2] - [:a 3] - [:c 4]] - (->> xs - (rx/seq->o) - (rx/group-by :k :v) - (rx/mapcat (fn [[k vo :as me]] - (is (instance? clojure.lang.MapEntry me)) - (rx/map #(vector k %) vo))) - (b/into []))))))) - -(deftest test-interleave - (are [inputs] (= (apply interleave inputs) - (->> (apply rx/interleave (map rx/seq->o inputs)) - (b/into []))) - [[] []] - [[] [1]] - [(range 5) (range 10) (range 10) (range 3)] - [(range 50) (range 10)] - [(range 5) (range 10 60) (range 10) (range 50)]) - - ; one-arg case, not supported by clojure.core/interleave - (is (= (range 10) - (->> (rx/interleave (rx/seq->o (range 10))) - (b/into []))))) - -(deftest test-interleave* - (are [inputs] (= (apply interleave inputs) - (->> (rx/interleave* (->> inputs - (map rx/seq->o) - (rx/seq->o))) - (b/into []))) - [[] []] - [[] [1]] - [(range 5) (range 10) (range 10) (range 3)] - [(range 50) (range 10)] - [(range 5) (range 10 60) (range 10) (range 50)])) - -(deftest test-interpose - (is (= (interpose \, [1 2 3]) - (b/into [] (rx/interpose \, (rx/seq->o [1 2 3])))))) - -(deftest test-into - (are [input to] (= (into to input) - (b/single (rx/into to (rx/seq->o input)))) - [6 7 8] [9 10 [11]] - #{} [1 2 3 2 4 5] - {} [[1 2] [3 2] [4 5]] - {} [] - '() (range 50))) - -(deftest test-iterate - (are [f x n] (= (->> (iterate f x) (take n)) - (->> (rx/iterate f x) (rx/take n) (b/into []))) - inc 0 10 - dec 20 100 - #(conj % (count %)) [] 5 - #(cons (count %) % ) nil 5)) - -(deftest test-keep - (is (= (into [] (keep identity [true true false])) - (b/into [] (rx/keep identity (rx/seq->o [true true false]))))) - - (is (= (into [] (keep #(if (even? %) (* 2 %)) (range 9))) - (b/into [] (rx/keep #(if (even? %) (* 2 %)) (rx/seq->o (range 9))))))) - -(deftest test-keep-indexed - (is (= (into [] (keep-indexed (fn [i v] - (if (even? i) v)) - [true true false])) - (b/into [] (rx/keep-indexed (fn [i v] - (if (even? i) v)) - (rx/seq->o [true true false])))))) - -(deftest test-map - (is (= (into {} (map (juxt identity name) - [:q :r :s :t :u])) - (b/into {} (rx/map (juxt identity name) - (rx/seq->o [:q :r :s :t :u]))))) - (is (= (into [] (map vector - [:q :r :s :t :u] - (range 10) - ["a" "b" "c" "d" "e"] )) - (b/into [] (rx/map vector - (rx/seq->o [:q :r :s :t :u]) - (rx/seq->o (range 10) ) - (rx/seq->o ["a" "b" "c" "d" "e"] ))))) - ; check > 4 arg case - (is (= (into [] (map vector - [:q :r :s :t :u] - [:q :r :s :t :u] - [:q :r :s :t :u] - (range 10) - (range 10) - (range 10) - ["a" "b" "c" "d" "e"] - ["a" "b" "c" "d" "e"] - ["a" "b" "c" "d" "e"])) - (b/into [] (rx/map vector - (rx/seq->o [:q :r :s :t :u]) - (rx/seq->o [:q :r :s :t :u]) - (rx/seq->o [:q :r :s :t :u]) - (rx/seq->o (range 10)) - (rx/seq->o (range 10)) - (rx/seq->o (range 10)) - (rx/seq->o ["a" "b" "c" "d" "e"]) - (rx/seq->o ["a" "b" "c" "d" "e"]) - (rx/seq->o ["a" "b" "c" "d" "e"])))))) - -(deftest test-map* - (is (= [[1 2 3 4 5 6 7 8]] - (b/into [] (rx/map* vector - (rx/seq->o [(rx/seq->o [1]) - (rx/seq->o [2]) - (rx/seq->o [3]) - (rx/seq->o [4]) - (rx/seq->o [5]) - (rx/seq->o [6]) - (rx/seq->o [7]) - (rx/seq->o [8])])))))) -(deftest test-map-indexed - (is (= (map-indexed vector [:a :b :c]) - (b/into [] (rx/map-indexed vector (rx/seq->o [:a :b :c]))))) - (testing "exceptions from fn have error value injected" - (try - (->> (rx/seq->o [:a :b :c]) - (rx/map-indexed (fn [i v] - (if (= 1 i) - (throw (java.io.FileNotFoundException. "blah"))) - v)) - (b/into [])) - (catch java.io.FileNotFoundException e - (is (= :b (-> e .getCause .getValue))))))) - -(deftest test-mapcat* - (let [f (fn [a b c d e] - [(+ a b) (+ c d) e])] - (is (= (->> (range 5) - (map (fn [_] (range 5))) - (apply mapcat f)) - (->> (range 5) - (map (fn [_] (rx/seq->o (range 5)))) - (rx/seq->o) - (rx/mapcat* (fn [& args] (rx/seq->o (apply f args)))) - (b/into [])))))) - -(deftest test-mapcat - (let [f (fn [v] [v (* v v)]) - xs (range 10)] - (is (= (mapcat f xs) - (b/into [] (rx/mapcat (comp rx/seq->o f) (rx/seq->o xs)))))) - - (let [f (fn [a b] [a b (* a b)]) - as (range 10) - bs (range 15)] - (is (= (mapcat f as bs) - (b/into [] (rx/mapcat (comp rx/seq->o f) - (rx/seq->o as) - (rx/seq->o bs))))))) - -(deftest test-next - (let [in [:q :r :s :t :u]] - (is (= (next in) (b/into [] (rx/next (rx/seq->o in))))))) - -(deftest test-nth - (is (= [:a] - (b/into [] (rx/nth (rx/seq->o [:s :b :a :c]) 2)))) - (is (= [:fallback] - (b/into [] (rx/nth (rx/seq->o [:s :b :a :c]) 25 :fallback))))) - -(deftest test-rest - (let [in [:q :r :s :t :u]] - (is (= (rest in) (b/into [] (rx/rest (rx/seq->o in))))))) - -(deftest test-partition-all - (are [input-size part-size step] (= (->> (range input-size) - (partition-all part-size step)) - (->> (range input-size) - (rx/seq->o) - (rx/partition-all part-size step) - (rx/map #(rx/into [] %)) - (rx/concat*) - (b/into []))) - 0 1 1 - 10 2 2 - 10 3 2 - 15 30 4) - - (are [input-size part-size] (= (->> (range input-size) - (partition-all part-size)) - (->> (range input-size) - (rx/seq->o) - (rx/partition-all part-size) - (rx/map #(rx/into [] %)) - (rx/concat*) - (b/into []))) - 0 1 - 10 2 - 10 3 - 15 30)) - -(deftest test-range - (are [start end step] (= (range start end step) - (->> (rx/range start end step) (b/into []))) - 0 10 2 - 0 -100 -1 - 5 100 9) - - (are [start end] (= (range start end) - (->> (rx/range start end) (b/into []))) - 0 10 - 0 -100 - 5 100) - - (are [start] (= (->> (range start) (take 100)) - (->> (rx/range start) (rx/take 100) (b/into []))) - 50 - 0 - 5 - -20) - (is (= (->> (range) (take 500)) - (->> (rx/range) (rx/take 500) (b/into []))))) - -(deftest test-reduce - (is (= (reduce + 0 (range 4)) - (b/first (rx/reduce + 0 (rx/seq->o (range 4))))))) - -(deftest test-reductions - (is (= (into [] (reductions + 0 (range 4))) - (b/into [] (rx/reductions + 0 (rx/seq->o (range 4))))))) - -(deftest test-some - (is (= [:r] (b/into [] (rx/some #{:r :s :t} (rx/seq->o [:q :v :r]))))) - (is (= [] (b/into [] (rx/some #{:r :s :t} (rx/seq->o [:q :v])))))) - -(deftest test-sort - (are [in cmp] (= (if cmp - (sort cmp in) - (sort in)) - (->> in - (rx/seq->o) - (#(if cmp (rx/sort cmp %) (rx/sort %))) - (b/into []))) - [] nil - [] (comp - compare) - [3 1 2] nil - [1 2 3] nil - [1 2 3] (comp - compare) - [2 1 3] (comp - compare))) - -(deftest test-sort-by - (are [rin cmp] (let [in (map #(hash-map :foo %) rin)] - (= (if cmp - (sort-by :foo cmp in) - (sort-by :foo in)) - (->> in - (rx/seq->o) - (#(if cmp (rx/sort-by :foo cmp %) (rx/sort-by :foo %))) - (b/into [])))) - [] nil - [] (comp - compare) - [3 1 2] nil - [1 2 3] nil - [1 2 3] (comp - compare) - [2 1 3] (comp - compare))) - - -(deftest test-split-with - (is (= (split-with (partial >= 3) (range 6)) - (->> (rx/seq->o (range 6)) - (rx/split-with (partial >= 3)) - b/first - (map (partial b/into [])))))) - -(deftest test-take-while - (is (= (into [] (take-while even? [2 4 6 8 1 2 3])) - (b/into [] (rx/take-while even? (rx/seq->o [2 4 6 8 1 2 3])))))) - -(deftest test-throw - (let [expected (IllegalArgumentException. "HI") - called (atom nil)] - (rx/subscribe (rx/throw expected) - (fn [_]) - (fn [e] (reset! called expected)) - (fn [_])) - (is (identical? expected @called)))) - -(deftest test-catch* - (testing "Is just a passthrough if there's no error" - (is (= [1 2 3] - (->> (rx/seq->o [1 2 3]) - (rx/catch* Exception (fn [e] (throw "OH NO"))) - (b/into []))))) - - (testing "Can catch a particular exception type and continue with an observable" - (is (= [1 2 4 5 6 "foo"] - (->> (rx/generator [o] - (rx/on-next o 1) - (rx/on-next o 2) - (rx/on-error o (IllegalStateException. "foo"))) - (rx/catch* IllegalStateException - (fn [e] - (rx/seq->o [4 5 6 (.getMessage e)]))) - (b/into []))))) - - (testing "if exception isn't matched, it's passed to on-error" - (let [expected (IllegalArgumentException. "HI") - called (atom nil)] - (rx/subscribe (->> (rx/generator [o] - (rx/on-next o 1) - (rx/on-next o 2) - (rx/on-error o expected)) - (rx/catch* IllegalStateException (fn [e] - (rx/return "WAT?")))) - (fn [_]) - (fn [e] (reset! called expected)) - (fn [_])) - (is (identical? expected @called)))) - - (testing "if p returns Throwable, that's passed as e" - (let [cause (IllegalArgumentException. "HI") - wrapper (java.util.concurrent.ExecutionException. cause)] - (is (= [cause] - (->> (rx/throw wrapper) - (rx/catch #(.getCause %) e - (rx/return e)) - (b/into []))))))) - - -(deftest test-finally - (testing "Supports a finally clause" - (testing "called on completed" - (let [completed (atom nil) - called (atom nil)] - (rx/subscribe (->> (rx/seq->o [1 2 3]) - (rx/finally* (fn [] (reset! called (str "got it"))))) - (fn [_]) - (fn [_] (throw (IllegalStateException. "WAT"))) - (fn [] (reset! completed "DONE"))) - (is (= "got it" @called)) - (is (= "DONE" @completed)))) - - (testing "called on error" - (let [expected (IllegalStateException. "expected") - completed (atom nil) - called (atom nil)] - (rx/subscribe (->> (rx/generator [o] - (rx/on-next o 1) - (rx/on-next o 2) - (rx/on-error o expected)) - (rx/finally - (reset! called "got it"))) - (fn [_]) - (fn [e] (reset! completed e)) - (fn [] (throw (IllegalStateException. "WAT")))) - (is (= "got it" @called)) - (is (identical? expected @completed)))))) - - -;################################################################################ - -(deftest test-graph-imports - (is (= 99 - (-> {:a {:deps [] :factory (fn [_] (rx/return 99))}} - rx/let-o* - :a - b/single))) - (is (= 100 - (b/single (rx/let-o [?a (rx/return 100)] - ?a))))) - -;################################################################################ - -(deftest test-realized-imports - (is (= {:a 1 :b 2} - (->> (rx/let-realized [a (rx/return 1) - b (rx/return 2)] - {:a a :b b}) - b/single)))) - - diff --git a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/future_test.clj b/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/future_test.clj deleted file mode 100644 index ba2344e4e2..0000000000 --- a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/future_test.clj +++ /dev/null @@ -1,61 +0,0 @@ -(ns rx.lang.clojure.future-test - (:require [rx.lang.clojure.core :as rx] - [rx.lang.clojure.blocking :as b] - [rx.lang.clojure.future :as f]) - (:require [clojure.test :refer [deftest testing is]])) - -(deftest test-future-generator - (is (not= [(.getId (Thread/currentThread))] - (b/into [] - (f/future-generator* future-call - #(rx/on-next % (.getId (Thread/currentThread)))))))) - -(deftest test-future - (is (= [15] (b/into [] (f/future* future-call + 1 2 3 4 5))))) - -(deftest test-future-exception - (is (= "Caught: boo" - (->> (f/future* future-call #(throw (java.io.FileNotFoundException. "boo"))) - (rx/catch java.io.FileNotFoundException e - (rx/return (str "Caught: " (.getMessage e)))) - (b/single))))) - -(deftest test-future-cancel - (let [exited? (atom nil) - o (f/future* future-call - (fn [] (Thread/sleep 1000) - (reset! exited? true) - "WAT")) - result (->> o - (rx/take 0) - (b/into []))] - (Thread/sleep 2000) - (is (= [nil []] - [@exited? result])))) - -(deftest test-future-generator-cancel - (let [exited? (atom nil) - o (f/future-generator* future-call - (fn [o] - (rx/on-next o "FIRST") - (Thread/sleep 1000) - (reset! exited? true))) - result (->> o - (rx/take 1) - (b/into []))] - (Thread/sleep 2000) - (is (= [nil ["FIRST"]] - [@exited? result])))) - -(deftest test-future-generator-exception - (let [e (java.io.FileNotFoundException. "snake")] - (is (= [1 2 e] - (->> (f/future-generator* - future-call - (fn [o] - (rx/on-next o 1) - (rx/on-next o 2) - (throw e))) - (rx/catch java.io.FileNotFoundException e - (rx/return e)) - (b/into [])))))) diff --git a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/graph_test.clj b/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/graph_test.clj deleted file mode 100644 index 56ddfc9ff3..0000000000 --- a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/graph_test.clj +++ /dev/null @@ -1,122 +0,0 @@ -(ns rx.lang.clojure.graph-test - (:require [rx.lang.clojure.graph :as graph] - [rx.lang.clojure.core :as rx] - [rx.lang.clojure.future :as rx-future] - [rx.lang.clojure.blocking :as rx-blocking] - [clojure.test :refer [deftest testing is]])) - -(deftest test-let-o* - (testing "throws on cycle" - (is (thrown-with-msg? IllegalArgumentException #"Cycle found" - (graph/let-o* {:a {:deps [:a]}})))) - - (testing "throws on unknown" - (is (thrown-with-msg? IllegalArgumentException #"Unknown node" - (graph/let-o* {:a {:deps [:b]}})))) - - (testing "it works in a simple case" - (let [d {:a {:deps [] - :factory (fn [_] (rx/seq->o [1 2 3 4 5]))} - :b {:deps [:a] - :factory (fn [{:keys [a]}] (rx/map #(* % %) a)) } - :c {:deps [:a :b] - :factory (fn [{:keys [a b]}] (rx/map #(+ %1 %2) a b)) } - :d {:deps [:c :b] - :factory (fn [{:keys [c b]}] (rx/map #(+ %1 %2) c b)) } - } - f (graph/let-o* d) ] - (println f) - ; (n^2 + n) + n^2 - (is (= [3 10 21 36 55] - (rx-blocking/into [] (:d f))))))) - -(deftest test-let-o - (testing "it works" - (let [f (graph/let-o [?a (rx/seq->o [1 2 3]) - ?b (rx/seq->o [4 5 6])] - (rx/map + ?a ?b))] - (is (= [5 7 9] - (rx-blocking/into [] f))))) - - (testing "it still works" - (is (= {:a 99 :b 100 :z "hi"} - (rx-blocking/single - (-> (let [z (rx/return "hi")] ; an observable from "somewhere else" - (graph/let-o - [?a (rx-future/future* future-call #(do (Thread/sleep 50) 99)) - ?b (rx-future/future* future-call #(do (Thread/sleep 500) 100)) - ?c (rx/map #(hash-map :a %1 :b %2 :z %3) ?a ?b ?z) - ?z z] - (rx/reduce merge {} ?c))))))))) - -(deftest test-complicated-graph - ; These funcs model network requests for various stuff. They all return observable. - (let [request-vhs (fn [] - (rx-future/future-generator* - future-call - (fn [o] - (Thread/sleep 50) - (doseq [i (range 3)] - (rx/on-next o {:id i}))))) - request-user (fn [id] - (rx-future/future* - future-call - #(do (Thread/sleep (rand-int 250)) - {:id id - :name (str "friend" id) }))) - request-ab (fn [u] - (rx-future/future* - future-call - #(do (Thread/sleep (rand-int 250)) - {:user-id (:id u) - :cell (* 2 (:id u))}))) - - request-video-md (fn [v] - (rx/return {:video v - :title (str "title" (:id v)) })) - - ; Now we can stitch all these requests together into an rx graph to - ; produce a response. - o (graph/let-o [?user-info (rx-future/future* - future-call - #(do (Thread/sleep 20) - {:name "Bob" - :id 12345 - :friend-ids [1 2 3] })) - - ?friends (->> ?user-info - (rx/mapcat (fn [ui] - (rx/mapcat request-user - (rx/seq->o (:friend-ids ui)))))) - - ?ab (->> (rx/concat ?user-info ?friends) - (rx/mapcat request-ab)) - - ?ab-lookup (->> ?ab - (rx/map (juxt :user-id #(dissoc % :user-id))) - (rx/into {})) - - ?vhs (request-vhs) - - - ?metadata (->> ?vhs - (rx/mapcat request-video-md))] - (rx/map (fn [u m f ab-lookup] - {:user (dissoc u :friend-ids) - :videos m - :friends (sort-by :id f) - :ab ab-lookup}) - ?user-info - (rx/into [] ?metadata) - (rx/into [] ?friends) - ?ab-lookup))] - - (is (= {:user {:name "Bob" :id 12345} - :videos [{:video {:id 0} :title "title0"} - {:video {:id 1} :title "title1"} - {:video {:id 2} :title "title2"}] - :friends [{:name "friend1" :id 1}{:name "friend2" :id 2}{:name "friend3" :id 3}] - :ab {12345 {:cell 24690} 1 {:cell 2} 2 {:cell 4} 3 {:cell 6}} } - (rx-blocking/single o))))) - - 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 deleted file mode 100644 index 811ec94728..0000000000 --- a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/interop_test.clj +++ /dev/null @@ -1,159 +0,0 @@ -(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] - [rx.lang.clojure.interop DummyObservable])) - -(deftest test-fn* - (testing "implements Func0-9" - (let [f (rx/fn* vector)] - (is (instance? rx.Observable$OnSubscribeFunc f)) - (is (instance? rx.functions.Func0 f)) - (is (instance? rx.functions.Func1 f)) - (is (instance? rx.functions.Func2 f)) - (is (instance? rx.functions.Func3 f)) - (is (instance? rx.functions.Func4 f)) - (is (instance? rx.functions.Func5 f)) - (is (instance? rx.functions.Func6 f)) - (is (instance? rx.functions.Func7 f)) - (is (instance? rx.functions.Func8 f)) - (is (instance? rx.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))))) - - (let [dummy (DummyObservable.)] - (testing "preserves metadata applied to form" - ; No type hint, picks Object overload - (is (= "Object" - (.call dummy (rx/fn* +)))) - (is (= "rx.functions.Func1" - (.call dummy - ^rx.functions.Func1 (rx/fn* +)))) - (is (= "rx.functions.Func2" - (.call dummy - ^rx.functions.Func2 (rx/fn* *))))))) - -(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))))) - - (let [dummy (DummyObservable.)] - (testing "preserves metadata applied to form" - ; No type hint, picks Object overload - (is (= "Object" - (.call dummy - (rx/fn [a] a)))) - (is (= "rx.functions.Func1" - (.call dummy - ^rx.functions.Func1 (rx/fn [a] a)))) - (is (= "rx.functions.Func2" - (.call dummy - ^rx.functions.Func2 (rx/fn [a b] (* a b)))))))) - - -(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.Observable$OnSubscribe a)) - (is (instance? rx.functions.Action0 a)) - (is (instance? rx.functions.Action1 a)) - (is (instance? rx.functions.Action2 a)) - (is (instance? rx.functions.Action3 a)) - (.call a) - (.call a 1) - (.call a 1 2) - (.call a 1 2 3) - (is (= [[] [1] [1 2] [1 2 3]])))) - (let [dummy (DummyObservable.)] - (testing "preserves metadata applied to form" - ; no meta, picks Object overload - (is (= "Object" - (.call dummy - (rx/action* println)))) - (is (= "rx.functions.Action1" - (.call dummy - ^rx.functions.Action1 (rx/action* println)))) - (is (= "rx.functions.Action2" - (.call dummy - ^rx.functions.Action2 (rx/action* prn))))))) - -(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)))) - - (let [dummy (DummyObservable.)] - (testing "preserves metadata applied to form" - ; no meta, picks Object overload - (is (= "Object" - (.call dummy - (rx/action [a] a)))) - (is (= "rx.functions.Action1" - (.call dummy - ^rx.functions.Action1 (rx/action [a] a)))) - (is (= "rx.functions.Action2" - (.call dummy - ^rx.functions.Action2 (rx/action [a b] (* a b)))))))) - -(deftest test-basic-usage - - (testing "can create an observable with old style fn" - (is (= 99 - (-> (Observable/create (rx/fn [^rx.Observer o] - (.onNext o 99) - (.onCompleted o) - (rx.subscriptions.Subscriptions/empty))) - .toBlockingObservable - .single)))) - - (testing "can create an observable with new-style action" - (is (= 99 - (-> (Observable/create (rx/action [^rx.Subscriber s] - (when-not (.isUnsubscribed s) - (.onNext s 99)) - (.onCompleted s))) - .toBlockingObservable - .single)))) - (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* +)) - .toBlockingObservable - .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/realized_test.clj b/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/realized_test.clj deleted file mode 100644 index 3bf9b16872..0000000000 --- a/language-adaptors/rxjava-clojure/src/test/clojure/rx/lang/clojure/realized_test.clj +++ /dev/null @@ -1,130 +0,0 @@ -(ns rx.lang.clojure.realized-test - (:require [rx.lang.clojure.realized :as r] - [rx.lang.clojure.core :as rx] - [rx.lang.clojure.future :as rx-future] - [rx.lang.clojure.blocking :as rx-blocking] - [clojure.test :refer [deftest testing is]])) - - - -(deftest test-realized-map - (testing "Turns map of observables into observable of map" - (let [o (r/realized-map :a (r/all (rx/seq->o [1 2 3])) - :a2 (rx/seq->o [99 100 101]) - :b (rx/return "hi") - :c [(->> [1 2 3] - rx/seq->o - (rx/map #(* % %))) - next] - :d (rx/return "just one") - :e "just a value") - result (rx-blocking/single o)] - (is (= {:a [1 2 3] - :a2 101 - :b "hi" - :c [4 9] - :d "just one" - :e "just a value" } - result))))) - -(deftest test-realized-map - (testing "works like realized-map, but takes a map instead of key/value pairs" - (is (= {:a [1 2] - :b 500 } - (->> {:a (r/all (rx/seq->o [1 2])) - :b 500 } - r/realized-map* - rx-blocking/single))))) - -(deftest test-let-realized - (is (= {:a* 2 - :b* 500 - :c* 1000 } - (->> (r/let-realized [a [(rx/seq->o [1 2]) last] - b 500 - c (rx/return 1000) ] - {:a* a - :b* b - :c* c }) - rx-blocking/single)))) - -(deftest test-only - (testing "raises IllegalStateException if sequence is empty" - (is (thrown-with-msg? IllegalStateException #"did not produce" - (->> (r/let-realized [a (rx/seq->o [1 2])] - {:a a}) - rx-blocking/single))) - ; Just to be sure, make sure it goes through onError. - (let [values (atom []) - errors (atom [])] - (rx/subscribe (r/let-realized [a (rx/seq->o [1 2])] - {:a a}) - #(swap! values conj %) - #(swap! errors conj %)) - (is (empty? @values)) - (is (= 1 (count @errors))) - (let [[e] @errors] - (is (instance? IllegalStateException e)))))) - -(deftest test-all - (testing "collects all values from an observable" - (is (= [1 2 3] - (->> (r/let-realized [a (r/all (rx/seq->o [1 2 3]))] - a) - rx-blocking/single))))) - -; Playing with some expressing some of the video stuff with this. -(comment - (->> (get-list-of-lists user-id) - (rx/mapcat (fn [list] - (->> (video-list->videos list) - (rx/take 10)))) - (rx/mapcat (fn [video] - (->> (r/let-realized [md (video->metadata video) - bm (video->bookmark video) - rt (video->rating video user-id)] - {:id (:id video) - :title (:title md) - :length (:duration md) - :bookmark bm - :rating {:actual (:actual-star-rating rt) - :average (:average-star-rating rt) - :predicted (:predicted-star-rating rt) } }))))) - - (->> (get-list-of-lists user-id) - (rx/mapcat (fn [list] - (->> (video-list->videos list) - (rx/take 10)))) - (rx/mapcat (fn [video] - (->> (r/realized-map :md (video->metadata video) - :bm (video->bookmark video) - :rt (video->rating video user-id)) - (rx/map (fn [{:keys [md bm rt]}] - {:id (:id video) - :title (:title md) - :length (:duration md) - :bookmark bm - :rating {:actual (:actual-star-rating rt) - :average (:average-star-rating rt) - :predicted (:predicted-star-rating rt) } })))))) - - (->> (get-list-of-lists user-id) - (rx/mapcat (fn [list] - (->> (video-list->videos list) - (rx/take 10)))) - (rx/mapcat (fn [video] - (->> (r/realized-map :id (:id video) - :md [(video->metadata video) - first - #(select-keys % [:title :duration])] - :bookmark (video->bookmark video) - :rating [(video->rating video user-id) - first - #(hash-map :actual (:actual-star-rating %) - :average (:average-star-rating %) - :predicted (:predicted-star-rating %))]) - (rx/map (fn [m] - (-> m - (merge (:md m)) - (dissoc :md))))))))) - diff --git a/language-adaptors/rxjava-groovy/README.md b/language-adaptors/rxjava-groovy/README.md deleted file mode 100644 index 68768e8d3e..0000000000 --- a/language-adaptors/rxjava-groovy/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Groovy Adaptor for RxJava - - -This adaptor allows 'groovy.lang.Closure' functions to be used and RxJava will know how to invoke them. - -This enables code such as: - -```groovy - Observable.from("one", "two", "three") - .take(2) - .subscribe({arg -> println(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-groovy%22). - -Example for Maven: - -```xml -<dependency> - <groupId>com.netflix.rxjava</groupId> - <artifactId>rxjava-groovy</artifactId> - <version>x.y.z</version> -</dependency> -``` - -and for Ivy: - -```xml -<dependency org="com.netflix.rxjava" name="rxjava-groovy" rev="x.y.z" /> -``` - -and for Gradle: - -```groovy -compile 'com.netflix.rxjava:rxjava-groovy:x.y.z' -``` diff --git a/language-adaptors/rxjava-groovy/build.gradle b/language-adaptors/rxjava-groovy/build.gradle deleted file mode 100644 index 62388bb60a..0000000000 --- a/language-adaptors/rxjava-groovy/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -apply plugin: 'groovy' -apply plugin: 'osgi' - -dependencies { - compile project(':rxjava-core') - compile 'org.codehaus.groovy:groovy-all:2.+' - testCompile 'junit:junit-dep:4.10' - testCompile 'org.mockito:mockito-core:1.8.5' -} - -jar { - manifest { - name = 'rxjava-groovy' - 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' - } -} diff --git a/language-adaptors/rxjava-groovy/src/examples/groovy/rx/lang/groovy/examples/RxExamples.groovy b/language-adaptors/rxjava-groovy/src/examples/groovy/rx/lang/groovy/examples/RxExamples.groovy deleted file mode 100644 index 8d7eabb3d7..0000000000 --- a/language-adaptors/rxjava-groovy/src/examples/groovy/rx/lang/groovy/examples/RxExamples.groovy +++ /dev/null @@ -1,231 +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.groovy.examples; - -import rx.Observable; -import rx.Observer; -import rx.Subscription; -import rx.subscriptions.Subscriptions; -import rx.functions.Action0 -import rx.functions.Func1; - -// -------------------------------------------------- -// Hello World! -// -------------------------------------------------- - -def hello(String[] names) { - Observable.from(names) - .subscribe({ println "Hello " + it + "!"}) -} - -hello("Ben", "George") - - -// -------------------------------------------------- -// Create Observables from Existing Data -// -------------------------------------------------- - -def existingDataFromNumbers() { - Observable<Integer> o = Observable.from(1, 2, 3, 4, 5, 6); -} - -def existingDataFromNumbersUsingFrom() { - Observable<Integer> o2 = Observable.from(1, 2, 3, 4, 5, 6); -} - -def existingDataFromObjects() { - Observable<String> o = Observable.from("a", "b", "c"); -} - -def existingDataFromObjectsUsingFrom() { - Observable<String> o = Observable.from("a", "b", "c"); -} - -def existingDataFromList() { - def list = [5, 6, 7, 8] - Observable<Integer> o = Observable.from(list); -} - -def existingDataFromListUsingFrom() { - def list = [5, 6, 7, 8] - Observable<Integer> o2 = Observable.from(list); -} - -def existingDataWithJust() { - Observable<String> o = Observable.just("one object"); -} - - -// -------------------------------------------------- -// Create Custom Observables -// -------------------------------------------------- - - -/** - * This example shows a custom Observable that blocks - * when subscribed to (does not spawn an extra thread). - * - * @return Observable<String> - */ -def customObservableBlocking() { - return Observable.create(new Func1<Observer<String>, Subscription>() { - def Subscription call(Observer<String> observer) { - for(int i=0; i<50; i++) { - observer.onNext("value_" + i); - } - // after sending all values we complete the sequence - observer.onCompleted(); - // return an empty subscription since this blocks and thus - // can't be unsubscribed from - return Subscriptions.empty(); - }; - }); -} - -// To see output: -customObservableBlocking().subscribe({ println(it)}); - -/** - * This example shows a custom Observable that does not block - * when subscribed to as it spawns a separate thread. - * - * @return Observable<String> - */ -def customObservableNonBlocking() { - return Observable.create(new Func1<Observer<String>, Subscription>() { - /** - * This 'call' method will be invoked with the Observable is subscribed to. - * - * It spawns a thread to do it asynchronously. - */ - def Subscription call(Observer<String> observer) { - // For simplicity this example uses a Thread instead of an ExecutorService/ThreadPool - final Thread t = new Thread(new Runnable() { - void run() { - for(int i=0; i<75; i++) { - observer.onNext("anotherValue_" + i); - } - // after sending all values we complete the sequence - observer.onCompleted(); - }; - }); - t.start(); - - return Subscriptions.create(new Action0() { - public void call() { - // Ask the thread to stop doing work. - // For this simple example it just interrupts. - t.interrupt(); - } - }); - }; - }); -} - -// To see output: -customObservableNonBlocking().subscribe({ println(it)}); - - -/** - * Fetch a list of Wikipedia articles asynchronously. - * - * @param wikipediaArticleName - * @return Observable<String> of HTML - */ -def fetchWikipediaArticleAsynchronously(String... wikipediaArticleNames) { - return Observable.create({ Observer<String> observer -> - Thread.start { - for(articleName in wikipediaArticleNames) { - observer.onNext(new URL("http://en.wikipedia.org/wiki/"+articleName).getText()); - } - observer.onCompleted(); - } - return Subscriptions.empty(); - }); -} - -// To see output: -fetchWikipediaArticleAsynchronously("Tiger", "Elephant") - .subscribe({ println "--- Article ---\n" + it.substring(0, 125)}) - - -// -------------------------------------------------- -// Composition -// -------------------------------------------------- - -/** - * Asynchronously calls 'customObservableNonBlocking' and defines - * a chain of operators to apply to the callback sequence. - */ -def simpleComposition() { - customObservableNonBlocking() - .skip(10) - .take(5) - .map({ stringValue -> return stringValue + "_transformed"}) - .subscribe({ println "onNext => " + it}) -} - -// To see output: -simpleComposition(); - -/* - -(defn simpleComposition [] - "Asynchronously calls 'customObservableNonBlocking' and defines - a chain of operators to apply to the callback sequence." - (-> - (customObservableNonBlocking) - (.skip 10) - (.take 5) - (.map #(do (str % "_transformed"))) - (.subscribe #(println "onNext =>" %)))) - */ - - - -// -------------------------------------------------- -// Error Handling -// -------------------------------------------------- - - - -/** - * Fetch a list of Wikipedia articles asynchronously with error handling. - * - * @param wikipediaArticleName - * @return Observable<String> of HTML - */ -def fetchWikipediaArticleAsynchronouslyWithErrorHandling(String... wikipediaArticleNames) { - return Observable.create({ Observer<String> observer -> - Thread.start { - try { - for(articleName in wikipediaArticleNames) { - observer.onNext(new URL("http://en.wikipedia.org/wiki/"+articleName).getText()); - } - observer.onCompleted(); - } catch(Exception e) { - observer.onError(e); - } - } - return Subscriptions.empty(); - }); -} - -fetchWikipediaArticleAsynchronouslyWithErrorHandling("Tiger", "NonExistentTitle", "Elephant") - .subscribe( - { println "--- Article ---\n" + it.substring(0, 125)}, - { println "--- Error ---\n" + it.getMessage()}) - diff --git a/language-adaptors/rxjava-groovy/src/examples/groovy/rx/lang/groovy/examples/VideoExample.groovy b/language-adaptors/rxjava-groovy/src/examples/groovy/rx/lang/groovy/examples/VideoExample.groovy deleted file mode 100644 index 5323349fb1..0000000000 --- a/language-adaptors/rxjava-groovy/src/examples/groovy/rx/lang/groovy/examples/VideoExample.groovy +++ /dev/null @@ -1,259 +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.groovy.examples; - -import rx.Observable; -import rx.Observer; -import rx.Subscription; -import rx.subscriptions.BooleanSubscription; -import rx.functions.Func1; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -class VideoExample { - -static void main(String[] args) { - VideoExample v = new VideoExample(); - println("---- sequence of video dictionaries ----") - v.getVideoGridForDisplay(1).subscribe( - { videoDictionary -> // onNext - // this will print the dictionary for each video - // and is a good representation of how progressive rendering could work - println(videoDictionary) }, - { exception -> // onError - println("Error: " + exception) }, - { // onCompleted - v.executor.shutdownNow(); - }); - - VideoExample v2 = new VideoExample(); - v2.getVideoGridForDisplay(1).toList().subscribe( - { videoDictionaryList -> // onNext - // this will be called once with a list - // and demonstrates how a sequence can be combined - // for document style responses (most webservices) - println("\n ---- single list of video dictionaries ----\n" + videoDictionaryList) }, - { exception -> // onError - println("Error: " + exception) }, - { // onCompleted - v2.executor.shutdownNow(); - }); -} -/** - * Demonstrate how Rx is used to compose Observables together such as - * how a web service would to generate a JSON response. - * - * The simulated methods for the metadata represent different services - * that are often backed by network calls. - * - * This will return a sequence of dictionaries such as this: - * - * [id:1000, title:video-1000-title, length:5428, bookmark:0, - * rating:[actual:4, average:3, predicted:0]] - */ -Observable getVideoGridForDisplay(userId) { - // take the first 5 lists - getListOfLists(userId).take(5).mapMany({ VideoList list -> - // for each VideoList we want to fetch the videos - list.getVideos() - .take(10) // we only want the first 10 of each list - .mapMany({ Video video -> - // for each video we want to fetch metadata - def m = video.getMetadata().map({ Map<String, String> md -> - // transform to the data and format we want - return [title: md.get("title"), - length: md.get("duration")] - }) - def b = video.getBookmark(userId).map({ position -> - return [bookmark: position] - }) - def r = video.getRating(userId).map({ VideoRating rating -> - return [rating: - [actual: rating.getActualStarRating(), - average: rating.getAverageStarRating(), - predicted: rating.getPredictedStarRating()]] - }) - // compose these together - return Observable.zip(m, b, r, { - metadata, bookmark, rating -> - // now transform to complete dictionary of data - // we want for each Video - return [id: video.videoId] << metadata << bookmark << rating - }) - }) - }) -} - -/** - * Retrieve a list of lists of videos (grid). - * - * Observable<VideoList> is the "push" equivalent to List<VideoList> - */ -Observable<VideoList> getListOfLists(userId) { - return Observable.create({ observer -> - BooleanSubscription subscription = new BooleanSubscription(); - try { - // this will happen on a separate thread as it requires a network call - executor.execute({ - // simulate network latency - Thread.sleep(180); - for(i in 0..15) { - if(subscription.isUnsubscribed()) { - break; - } - try { - //println("****** emitting list: " + i) - observer.onNext(new VideoList(i)) - }catch(Exception e) { - observer.onError(e); - } - } - observer.onCompleted(); - }) - }catch(Exception e) { - observer.onError(e); - } - return subscription; - }) -} - -/** - * Represents a list of videos as part of a grid (list of lists). - */ -class VideoList { - - int listPosition; - VideoList(int position) { - this.listPosition = position - } - - String getListName() { - return "ListName-" + listPosition - } - - Integer getListPosition() { - return listPosition - } - - Observable<Video> getVideos() { - return Observable.create({ observer -> - // we already have the videos once a list is loaded - // so we won't launch another thread but return - // the sequence of videos via push - // NOTE: This will always execute all 50 even if take(2) asks for only 2 - // as it performs synchronously and is not lazy. - for(i in 0..50) { - //println("emitting video: " + i) - observer.onNext(new Video((listPosition*1000)+i)) - } - observer.onCompleted(); - }) - } -} - -class Video { - int videoId; - Video(int videoId) { - this.videoId = videoId; - } - - // synchronous - Observable<Map<String, String>> getMetadata() { - // simulate fetching metadata from an in-memory cache - // so it will not asynchronously execute on a thread but - // immediately return an Observable with the data - return Observable.create({ observer -> - observer.onNext([ - title: "video-" + videoId + "-title", - actors: ["actor1", "actor2"], - duration: 5428]) - observer.onCompleted(); - }); - } - - // asynchronous - Observable<Integer> getBookmark(userId) { - // simulate fetching the bookmark for this user - // that specifies the last played position if - // this video has been played before - return Observable.create({ observer -> - // this will happen on a separate thread as it requires a network call - executor.execute({ - // simulate network latency - Thread.sleep(4); - if(randint(6) > 1) { - // most of the time they haven't watched a movie - // so the position is 0 - observer.onNext(randint(0)); - } else { - observer.onNext(randint(4000)); - } - observer.onCompleted(); - }) - }) - } - - // asynchronous - Observable<VideoRating> getRating(userId) { - // simulate fetching the VideoRating for this user - return Observable.create({ observer -> - // this will happen on a separate thread as it requires a network call - executor.execute({ - // simulate network latency - Thread.sleep(10); - observer.onNext(new VideoRating(videoId, userId)) - observer.onCompleted(); - }) - }) - } -} - -class VideoRating { - int videoId, userId - VideoRating(videoId, userId) { - this.videoId = videoId; - this.userId = userId; - } - - Integer getPredictedStarRating() { - return randint(5) - } - - Integer getAverageStarRating() { - return randint(4) - } - - Integer getActualStarRating() { - return randint(5) - } -} - -ExecutorService executor = new ThreadPoolExecutor(4, 4, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>()); - -def randint(int max) { - return Math.round(Math.random() * max) -} - -def combine( Map... m ) { - m.collectMany { it.entrySet() }.inject( [:] ) { result, e -> - result << [ (e.key):e.value + ( result[ e.key ] ?: 0 ) ] - } - } - -} 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 deleted file mode 100644 index dbf99b943d..0000000000 --- a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyActionWrapper.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.groovy; - -import groovy.lang.Closure; -import rx.functions.Action; -import rx.functions.Action0; -import rx.functions.Action1; -import rx.functions.Action2; -import rx.functions.Action3; - -/** - * Concrete wrapper that accepts a {@link Closure} and produces any needed Rx {@link Action}. - * - * @param <T1> - * @param <T2> - * @param <T3> - * @param <T4> - */ -public class GroovyActionWrapper<T1, T2, T3, T4> implements Action, Action0, Action1<T1>, Action2<T1, T2>, Action3<T1, T2, T3> { - - private final Closure<Void> closure; - - public GroovyActionWrapper(Closure<Void> 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/GroovyCreateWrapper.java b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyCreateWrapper.java deleted file mode 100644 index a5149c857e..0000000000 --- a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyCreateWrapper.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.groovy; - -import groovy.lang.Closure; -import rx.Observable.OnSubscribe; -import rx.Subscriber; -import rx.Subscription; - -public class GroovyCreateWrapper<T> implements OnSubscribe<T> { - - private final Closure<Void> closure; - - public GroovyCreateWrapper(Closure<Void> closure) { - this.closure = closure; - } - - @Override - public void call(Subscriber<? super T> op) { - Object o = closure.call(op); - /* - * If the new signature is being used, we will get NULL back. - * If the old is being used we will get a Subscription back. - */ - if (o != null) { - op.add((Subscription) o); - } - } - -} diff --git a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyFunctionWrapper.java b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyFunctionWrapper.java deleted file mode 100644 index e0b6132de2..0000000000 --- a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyFunctionWrapper.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.groovy; - -import groovy.lang.Closure; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.functions.Func2; -import rx.functions.Func3; -import rx.functions.Func4; -import rx.functions.Func5; -import rx.functions.Func6; -import rx.functions.Func7; -import rx.functions.Func8; -import rx.functions.Func9; -import rx.functions.FuncN; -import rx.functions.Function; - -/** - * Concrete wrapper that accepts a {@link Closure} and produces any needed Rx {@link Function}. - * - * @param <T1> - * @param <T2> - * @param <T3> - * @param <T4> - * @param <R> - */ -public class GroovyFunctionWrapper<T1, T2, T3, T4, T5, T6, T7, T8, T9, R> implements - Func0<R>, - Func1<T1, R>, - Func2<T1, T2, R>, - Func3<T1, T2, T3, R>, - Func4<T1, T2, T3, T4, R>, - Func5<T1, T2, T3, T4, T5, R>, - Func6<T1, T2, T3, T4, T5, T6, R>, - Func7<T1, T2, T3, T4, T5, T6, T7, R>, - Func8<T1, T2, T3, T4, T5, T6, T7, T8, R>, - Func9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R>, - FuncN<R> { - - private final Closure<R> closure; - - public GroovyFunctionWrapper(Closure<R> 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); - } - - @Override - public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { - return (R) closure.call(t1, t2, t3, t4, t5); - } - - @Override - public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { - return (R) closure.call(t1, t2, t3, t4, t5, t6); - } - - @Override - public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) { - return (R) closure.call(t1, t2, t3, t4, t5, t6, t7); - } - - @Override - public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) { - return (R) closure.call(t1, t2, t3, t4, t5, t6, t7, t8); - } - - @Override - public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) { - return (R) closure.call(t1, t2, t3, t4, t5, t6, t7, t8, t9); - } - - @Override - public R call(Object... args) { - return (R) closure.call(args); - } -} \ No newline at end of file diff --git a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyOnSubscribeFuncWrapper.java b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyOnSubscribeFuncWrapper.java deleted file mode 100644 index 3e2bc1b638..0000000000 --- a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/GroovyOnSubscribeFuncWrapper.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.groovy; - -import groovy.lang.Closure; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscription; - -/** - * Concrete wrapper that accepts a {@link Closure} and produces a {@link OnSubscribeFunc}. - * - * @param <T> - */ -public class GroovyOnSubscribeFuncWrapper<T> implements OnSubscribeFunc<T> { - - private final Closure<Subscription> closure; - - public GroovyOnSubscribeFuncWrapper(Closure<Subscription> closure) { - this.closure = closure; - } - - @Override - public Subscription onSubscribe(Observer<? super T> observer) { - return closure.call(observer); - } - -} \ 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 deleted file mode 100644 index 0df587bfb5..0000000000 --- a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyExtensionModule.java +++ /dev/null @@ -1,204 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.groovy; - -import groovy.lang.Closure; -import groovy.lang.MetaMethod; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import org.codehaus.groovy.reflection.CachedClass; -import org.codehaus.groovy.reflection.ReflectionCache; -import org.codehaus.groovy.runtime.m12n.ExtensionModule; - -import rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.functions.Action; -import rx.functions.Function; -import rx.observables.BlockingObservable; - -/** - * 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"); - } - - @SuppressWarnings("rawtypes") - @Override - public List<MetaMethod> getMetaMethods() { - // System.out.println("**** RxGroovyExtensionModule => Initializing and returning MetaMethods."); - List<MetaMethod> methods = new ArrayList<MetaMethod>(); - - 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) { - if (m.getDeclaringClass().equals(Observable.class) && m.getName().equals("create")) { - return specialCasedOverrideForCreate(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 if (OnSubscribeFunc.class.isAssignableFrom(m.getParameterTypes()[i])) { - newArgs[i] = new GroovyOnSubscribeFuncWrapper((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; - } - }; - } - - /** - * Special case until we finish migrating off the deprecated 'create' method signature - */ - private MetaMethod specialCasedOverrideForCreate(final Method m) { - return new MetaMethod() { - - @Override - public int getModifiers() { - return m.getModifiers(); - } - - @Override - public String getName() { - return m.getName(); - } - - @Override - public Class<?> getReturnType() { - return m.getReturnType(); - } - - @Override - public CachedClass getDeclaringClass() { - return ReflectionCache.getCachedClass(m.getDeclaringClass()); - } - - @Override - @SuppressWarnings("unchecked") - public Object invoke(Object object, final Object[] arguments) { - return Observable.create(new GroovyCreateWrapper((Closure) arguments[0])); - } - - @SuppressWarnings("rawtypes") - @Override - public CachedClass[] getParameterTypes() { - Class[] pts = m.getParameterTypes(); - CachedClass[] cc = new CachedClass[pts.length]; - for (int i = 0; i < pts.length; i++) { - if (Function.class.isAssignableFrom(pts[i])) { - // function type to be replaced by closure - cc[i] = ReflectionCache.getCachedClass(Closure.class); - } else { - // non-function type - cc[i] = ReflectionCache.getCachedClass(pts[i]); - } - } - return cc; - } - }; - } - -} diff --git a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyPropertiesModuleFactory.java b/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyPropertiesModuleFactory.java deleted file mode 100644 index ec80b6401e..0000000000 --- a/language-adaptors/rxjava-groovy/src/main/java/rx/lang/groovy/RxGroovyPropertiesModuleFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.groovy; - -import java.util.Properties; - -import org.codehaus.groovy.runtime.m12n.ExtensionModule; -import org.codehaus.groovy.runtime.m12n.PropertiesModuleFactory; - -/** - * Factory for {@link RxGroovyExtensionModule} to add extension methods. - * <p> - * This is loaded from /META-INF/services/org.codehaus.groovy.runtime.ExtensionModule - * <p> - * The property is defined as: moduleFactory=rx.lang.groovy.RxGroovyPropertiesModuleFactory - */ -public class RxGroovyPropertiesModuleFactory extends PropertiesModuleFactory { - - @Override - public ExtensionModule newModule(Properties properties, ClassLoader classLoader) { - return new RxGroovyExtensionModule(); - } - -} 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 deleted file mode 100644 index 975068e80c..0000000000 --- a/language-adaptors/rxjava-groovy/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index e459e14de4..0000000000 --- a/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/ObservableTests.groovy +++ /dev/null @@ -1,536 +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.groovy - -import static org.junit.Assert.* -import static org.mockito.Matchers.* -import static org.mockito.Mockito.* - -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.Observable.OnSubscribeFunc -import rx.subscriptions.Subscriptions - -def class ObservableTests { - - @Mock - ScriptAssertion a; - - @Mock - Observer<Integer> w; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testCreate() { - Observable.create({it.onNext('hello');it.onCompleted();}).subscribe({ result -> a.received(result)}); - verify(a, times(1)).received("hello"); - } - - @Test - public void testFilter() { - 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); - } - - @Test - public void testLast() { - assertEquals("three", Observable.from("one", "two", "three").toBlockingObservable().last()) - } - - @Test - public void testLastWithPredicate() { - assertEquals("two", Observable.from("one", "two", "three").toBlockingObservable().last({ x -> x.length() == 3})) - } - - @Test - public void testMap1() { - Observable.from(1).map({v -> 'hello_' + v}).subscribe({ result -> a.received(result)}); - verify(a, times(1)).received("hello_1"); - } - - @Test - public void testMap2() { - 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); - } - - @Test - public void testMaterialize() { - 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)); - } - - @Test - public void testMergeDelayError() { - Observable.mergeDelayError( - Observable.from(1, 2, 3), - Observable.merge( - Observable.from(6), - Observable.error(new NullPointerException()), - Observable.from(7)), - Observable.from(4, 5)) - .subscribe( { result -> a.received(result)}, { exception -> a.error(exception)}); - - verify(a, times(1)).received(1); - verify(a, times(1)).received(2); - verify(a, times(1)).received(3); - verify(a, times(1)).received(4); - verify(a, times(1)).received(5); - verify(a, times(1)).received(6); - verify(a, times(0)).received(7); - verify(a, times(1)).error(any(NullPointerException.class)); - } - - @Test - public void testMerge() { - Observable.merge( - Observable.from(1, 2, 3), - Observable.merge( - Observable.from(6), - Observable.error(new NullPointerException()), - Observable.from(7)), - Observable.from(4, 5)) - .subscribe({ result -> a.received(result)}, { exception -> a.error(exception)}); - - // executing synchronously so we can deterministically know what order things will come - verify(a, times(1)).received(1); - verify(a, times(1)).received(2); - verify(a, times(1)).received(3); - verify(a, times(0)).received(4); // the NPE will cause this sequence to be skipped - verify(a, times(0)).received(5); // the NPE will cause this sequence to be skipped - verify(a, times(1)).received(6); // this comes before the NPE so should exist - verify(a, times(0)).received(7);// this comes in the sequence after the NPE - verify(a, times(1)).error(any(NullPointerException.class)); - } - - @Test - public void testScriptWithMaterialize() { - new TestFactory().getObservable().materialize().subscribe({ result -> a.received(result)}); - // 2 times: once for hello_1 and once for onCompleted - verify(a, times(2)).received(any(Notification.class)); - } - - @Test - public void testScriptWithMerge() { - TestFactory f = new TestFactory(); - Observable.merge(f.getObservable(), f.getObservable()).subscribe({ result -> a.received(result)}); - verify(a, times(1)).received("hello_1"); - verify(a, times(1)).received("hello_2"); - } - - @Test - public void testFromWithIterable() { - def list = [1, 2, 3, 4, 5] - assertEquals(5, Observable.from(list).count().toBlockingObservable().single()); - } - - @Test - public void testFromWithObjects() { - def list = [1, 2, 3, 4, 5] - // this should now treat these as 2 objects so have a count of 2 - assertEquals(2, Observable.from(list, 6).count().toBlockingObservable().single()); - } - - /** - * Check that two different single arg methods are selected correctly - */ - @Test - public void testStartWith() { - def list = [10, 11, 12, 13, 14] - def startList = [1, 2, 3, 4, 5] - assertEquals(6, Observable.from(list).startWith(0).count().toBlockingObservable().single()); - assertEquals(10, Observable.from(list).startWith(startList).count().toBlockingObservable().single()); - } - - @Test - public void testScriptWithOnNext() { - new TestFactory().getObservable().subscribe({ result -> a.received(result)}); - verify(a).received("hello_1"); - } - - @Test - public void testSkipTake() { - 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); - } - - @Test - public void testSkip() { - 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); - } - - @Test - public void testTake() { - 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); - } - - @Test - public void testTakeLast() { - new TestFactory().getObservable().takeLast(1).subscribe({ result -> a.received(result)}); - verify(a, times(1)).received("hello_1"); - } - - @Test - public void testTakeWhileViaGroovy() { - 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); - } - - @Test - public void testTakeWhileWithIndexViaGroovy() { - 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); - } - - @Test - public void testToSortedList() { - new TestFactory().getNumbers().toSortedList().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 testForEach() { - Observable.create(new AsyncObservable()).toBlockingObservable().forEach({ result -> a.received(result)}); - verify(a, times(1)).received(1); - verify(a, times(1)).received(2); - verify(a, times(1)).received(3); - } - - @Test - public void testForEachWithError() { - try { - Observable.create(new AsyncObservable()).toBlockingObservable().forEach({ result -> throw new RuntimeException('err')}); - fail("we expect an exception to be thrown"); - }catch(Exception e) { - // do nothing as we expect this - } - } - - @Test - public void testLastOrDefault() { - def val = Observable.from("one", "two").toBlockingObservable().lastOrDefault("default", { x -> x.length() == 3}) - assertEquals("two", val) - } - - @Test - public void testLastOrDefault2() { - def val = Observable.from("one", "two").toBlockingObservable().lastOrDefault("default", { x -> x.length() > 3}) - assertEquals("default", val) - } - - public void testSingle1() { - def s = Observable.from("one").toBlockingObservable().single({ x -> x.length() == 3}) - assertEquals("one", s) - } - - @Test(expected = IllegalArgumentException.class) - public void testSingle2() { - Observable.from("one", "two").toBlockingObservable().single({ x -> x.length() == 3}) - } - - @Test - public void testDefer() { - def obs = Observable.from(1, 2) - Observable.defer({-> obs }).subscribe({ result -> a.received(result)}) - verify(a, times(1)).received(1); - verify(a, times(1)).received(2); - - } - - @Test - public void testAll() { - Observable.from(1, 2, 3).all({ x -> x > 0 }).subscribe({ result -> a.received(result) }); - verify(a, times(1)).received(true); - } - - - @Test - public void testZip() { - Observable o1 = Observable.from(1, 2, 3); - Observable o2 = Observable.from(4, 5, 6); - Observable o3 = Observable.from(7, 8, 9); - - List values = Observable.zip(o1, o2, o3, {a, b, c -> [a, b, c] }).toList().toBlockingObservable().single(); - assertEquals([1, 4, 7], values.get(0)); - assertEquals([2, 5, 8], values.get(1)); - assertEquals([3, 6, 9], values.get(2)); - } - - @Test - public void testZipWithIterable() { - Observable o1 = Observable.from(1, 2, 3); - Observable o2 = Observable.from(4, 5, 6); - Observable o3 = Observable.from(7, 8, 9); - - List values = Observable.zip([o1, o2, o3], {a, b, c -> [a, b, c] }).toList().toBlockingObservable().single(); - assertEquals([1, 4, 7], values.get(0)); - assertEquals([2, 5, 8], values.get(1)); - assertEquals([3, 6, 9], values.get(2)); - } - - @Test - public void testGroupBy() { - int count=0; - - Observable.from("one", "two", "three", "four", "five", "six") - .groupBy({String s -> s.length()}) - .flatMap({ - groupObservable -> - - return groupObservable.map({ - s -> - return "Value: " + s + " Group: " + groupObservable.getKey(); - }); - }).toBlockingObservable().forEach({ - s -> - println(s); - count++; - }) - - assertEquals(6, count); - } - - @Test - public void testToMap1() { - Map actual = new HashMap(); - - Observable.from("a", "bb", "ccc", "dddd") - .toMap({String s -> s.length()}) - .toBlockingObservable() - .forEach({s -> actual.putAll(s); }); - - Map expected = new HashMap(); - expected.put(1, "a"); - expected.put(2, "bb"); - expected.put(3, "ccc"); - expected.put(4, "dddd"); - - assertEquals(expected, actual); - } - - @Test - public void testToMap2() { - Map actual = new HashMap(); - - Observable.from("a", "bb", "ccc", "dddd") - .toMap({String s -> s.length()}, {String s -> s + s}) - .toBlockingObservable() - .forEach({s -> actual.putAll(s); }); - - Map expected = new HashMap(); - expected.put(1, "aa"); - expected.put(2, "bbbb"); - expected.put(3, "cccccc"); - expected.put(4, "dddddddd"); - - assertEquals(expected, actual); - } - - @Test - public void testToMap3() { - Map actual = new HashMap(); - - LinkedHashMap last3 = new LinkedHashMap() { - public boolean removeEldestEntry(Map.Entry e) { - return size() > 3; - } - }; - - Observable.from("a", "bb", "ccc", "dddd") - .toMap({String s -> s.length()}, {String s -> s + s}, { last3 }) - .toBlockingObservable() - .forEach({s -> actual.putAll(s); }); - - Map expected = new HashMap(); - expected.put(2, "bbbb"); - expected.put(3, "cccccc"); - expected.put(4, "dddddddd"); - - assertEquals(expected, actual); - } - @Test - public void testToMultimap1() { - Map actual = new HashMap(); - - Observable.from("a", "b", "cc", "dd") - .toMultimap({String s -> s.length()}) - .toBlockingObservable() - .forEach({s -> actual.putAll(s); }); - - Map expected = new HashMap(); - - expected.put(1, Arrays.asList("a", "b")); - expected.put(2, Arrays.asList("cc", "dd")); - - assertEquals(expected, actual); - } - - @Test - public void testToMultimap2() { - Map actual = new HashMap(); - - Observable.from("a", "b", "cc", "dd") - .toMultimap({String s -> s.length()}, {String s -> s + s}) - .toBlockingObservable() - .forEach({s -> actual.putAll(s); }); - - Map expected = new HashMap(); - - expected.put(1, Arrays.asList("aa", "bb")); - expected.put(2, Arrays.asList("cccc", "dddd")); - - assertEquals(expected, actual); - } - - @Test - public void testToMultimap3() { - Map actual = new HashMap(); - - LinkedHashMap last1 = new LinkedHashMap() { - public boolean removeEldestEntry(Map.Entry e) { - return size() > 1; - } - }; - - Observable.from("a", "b", "cc", "dd") - .toMultimap({String s -> s.length()}, {String s -> s + s}, { last1 }) - .toBlockingObservable() - .forEach({s -> actual.putAll(s); }); - - Map expected = new HashMap(); - - expected.put(2, Arrays.asList("cccc", "dddd")); - - assertEquals(expected, actual); - } - - @Test - public void testToMultimap4() { - Map actual = new HashMap(); - - LinkedHashMap last1 = new LinkedHashMap() { - public boolean removeEldestEntry(Map.Entry e) { - return size() > 2; - } - }; - - Observable.from("a", "b", "cc", "dd", "eee", "eee") - .toMultimap({String s -> s.length()}, {String s -> s + s}, { last1 }, - {i -> i == 2 ? new ArrayList() : new HashSet() }) - .toBlockingObservable() - .forEach({s -> actual.putAll(s); }); - - Map expected = new HashMap(); - - expected.put(2, Arrays.asList("cccc", "dddd")); - expected.put(3, new HashSet(Arrays.asList("eeeeee"))); - - assertEquals(expected, actual); - } - - def class AsyncObservable implements OnSubscribeFunc { - - public Subscription onSubscribe(final Observer<Integer> observer) { - new Thread(new Runnable() { - public void run() { - try { - Thread.sleep(50) - }catch(Exception e) { - // ignore - } - observer.onNext(1); - observer.onNext(2); - observer.onNext(3); - observer.onCompleted(); - } - }).start(); - return Subscriptions.empty(); - } - } - - def class TestFactory { - int counter = 1; - - public Observable<Integer> getNumbers() { - return Observable.from(1, 3, 2, 5, 4); - } - - public TestOnSubscribe getOnSubscribe() { - return new TestOnSubscribe(counter++); - } - - public Observable getObservable() { - return Observable.create(getOnSubscribe()); - } - } - - def interface ScriptAssertion { - public void error(Exception o); - - public void received(Object o); - } - - def class TestOnSubscribe implements OnSubscribeFunc<String> { - private final int count; - - public TestOnSubscribe(int count) { - this.count = count; - } - - public Subscription onSubscribe(Observer<String> observer) { - - observer.onNext("hello_" + count); - observer.onCompleted(); - - return Subscriptions.empty(); - } - } -} \ No newline at end of file diff --git a/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy b/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy deleted file mode 100644 index 414a992392..0000000000 --- a/language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/TestParallel.groovy +++ /dev/null @@ -1,36 +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.groovy - -import org.junit.Test - -import rx.Observable -import rx.Scheduler -import rx.schedulers.Schedulers -import rx.functions.Func1 - -class TestParallel { - - @Test - public void testParallelOperator() { - Observable.range(0, 100) - .parallel({ - it.map({ return it; }) - }) - .toBlockingObservable() - .forEach({ println("T: " + it + " Thread: " + Thread.currentThread()); }); - } -} diff --git a/language-adaptors/rxjava-jruby/README.md b/language-adaptors/rxjava-jruby/README.md deleted file mode 100644 index da75bac1fb..0000000000 --- a/language-adaptors/rxjava-jruby/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# JRuby Adaptor for RxJava - -This adaptor improves the success and performance of RxJava when Ruby `Proc` is passed to an RxJava method. - -This enables correct and efficient execution of code such as: - -```ruby - Observable.from("one", "two", "three"). - take(2). - subscribe {|val| puts val} -``` - -# Usage - -Require the JAR file as usual. After requiring the JAR, you must also require the interop code: - -```ruby -require "rx/lang/jruby/interop" -``` - -# 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 -<dependency> - <groupId>com.netflix.rxjava</groupId> - <artifactId>rxjava-jruby</artifactId> - <version>x.y.z</version> -</dependency> -``` - -and for Ivy: - -```xml -<dependency org="com.netflix.rxjava" name="rxjava-jruby" rev="x.y.z" /> -``` - -and for Gradle: - -```groovy -compile 'com.netflix.rxjava:rxjava-jruby:x.y.z' -``` diff --git a/language-adaptors/rxjava-jruby/build.gradle b/language-adaptors/rxjava-jruby/build.gradle deleted file mode 100644 index 383194c758..0000000000 --- a/language-adaptors/rxjava-jruby/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ -apply plugin: 'osgi' - -repositories { - maven { - url 'http://deux.gemjars.org' - } - mavenCentral() -} - -configurations { - rspec -} - -sourceSets { - test { - resources { - srcDir 'src/spec/ruby' - } - } -} - -dependencies { - compile project(':rxjava-core') - compile 'org.jruby:jruby:1.7+' - testCompile 'junit:junit-dep:4.10' - testCompile 'org.mockito:mockito-core:1.8.5' - rspec 'org.jruby:jruby-complete:1.7.4' - rspec 'org.rubygems:rspec:2.14.1' -} - -/* // benjchristensen => commenting out until internal Netflix build system can handle ruby gems -task(rspec, type: JavaExec) { - main 'org.jruby.Main' - classpath configurations.rspec + runtimeClasspath - args 'classpath:bin/rspec', 'src/spec/ruby' -} - -tasks.build.dependsOn << 'rspec' -*/ - -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' - } -} diff --git a/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyActionWrapper.java b/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyActionWrapper.java deleted file mode 100644 index 9bfef70a4c..0000000000 --- a/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyActionWrapper.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.jruby; - -import org.jruby.Ruby; -import org.jruby.RubyProc; -import org.jruby.javasupport.JavaUtil; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.builtin.IRubyObject; - -import rx.functions.Action; -import rx.functions.Action0; -import rx.functions.Action1; -import rx.functions.Action2; -import rx.functions.Action3; - -/** - * Concrete wrapper that accepts a {@link RubyProc} and produces any needed Rx {@link Action}. - * - * @param <T1> - * @param <T2> - * @param <T3> - * @param <T4> - */ -public class JRubyActionWrapper<T1, T2, T3, T4> implements Action, Action0, Action1<T1>, Action2<T1, T2>, Action3<T1, T2, T3> { - - private final RubyProc proc; - private final ThreadContext context; - private final Ruby runtime; - - public JRubyActionWrapper(ThreadContext context, RubyProc proc) { - this.proc = proc; - this.context = context; - this.runtime = context.getRuntime(); - } - - @Override - public void call() { - IRubyObject[] array = new IRubyObject[0]; - proc.call(context, array); - } - - @Override - public void call(T1 t1) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1)}; - proc.call(context, array); - } - - @Override - public void call(T1 t1, T2 t2) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), - JavaUtil.convertJavaToRuby(runtime, t2)}; - proc.call(context, array); - } - - @Override - public void call(T1 t1, T2 t2, T3 t3) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), - JavaUtil.convertJavaToRuby(runtime, t2), - JavaUtil.convertJavaToRuby(runtime, t3)}; - proc.call(context, array); - } - -} diff --git a/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyFunctionWrapper.java b/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyFunctionWrapper.java deleted file mode 100644 index d9c5ae959e..0000000000 --- a/language-adaptors/rxjava-jruby/src/main/java/rx/lang/jruby/JRubyFunctionWrapper.java +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.jruby; - -import org.jruby.Ruby; -import org.jruby.RubyProc; -import org.jruby.javasupport.JavaUtil; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.builtin.IRubyObject; - -import rx.functions.Func0; -import rx.functions.Func1; -import rx.functions.Func2; -import rx.functions.Func3; -import rx.functions.Func4; -import rx.functions.Func5; -import rx.functions.Func6; -import rx.functions.Func7; -import rx.functions.Func8; -import rx.functions.Func9; -import rx.functions.FuncN; -import rx.functions.Function; - -/** - * Concrete wrapper that accepts a {@link RubyProc} and produces any needed Rx {@link Function}. - * - * @param <T1> - * @param <T2> - * @param <T3> - * @param <T4> - * @param <T5> - * @param <T6> - * @param <T7> - * @param <T8> - * @param <T9> - * @param <R> - */ -public class JRubyFunctionWrapper<T1, T2, T3, T4, T5, T6, T7, T8, T9, R> implements - Func0<R>, - Func1<T1, R>, - Func2<T1, T2, R>, - Func3<T1, T2, T3, R>, - Func4<T1, T2, T3, T4, R>, - Func5<T1, T2, T3, T4, T5, R>, - Func6<T1, T2, T3, T4, T5, T6, R>, - Func7<T1, T2, T3, T4, T5, T6, T7, R>, - Func8<T1, T2, T3, T4, T5, T6, T7, T8, R>, - Func9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R>, - FuncN<R> { - - private final RubyProc proc; - private final ThreadContext context; - private final Ruby runtime; - - public JRubyFunctionWrapper(ThreadContext context, RubyProc proc) { - this.proc = proc; - this.context = context; - this.runtime = context.getRuntime(); - } - - @Override - @SuppressWarnings("unchecked") - public R call() { - IRubyObject[] array = new IRubyObject[0]; - return (R) proc.call(context, array); - } - - @Override - @SuppressWarnings("unchecked") - public R call(T1 t1) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1)}; - return (R) proc.call(context, array); - } - - @Override - @SuppressWarnings("unchecked") - public R call(T1 t1, T2 t2) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), - JavaUtil.convertJavaToRuby(runtime, t2)}; - return (R) proc.call(context, array); - } - - @Override - @SuppressWarnings("unchecked") - public R call(T1 t1, T2 t2, T3 t3) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), - JavaUtil.convertJavaToRuby(runtime, t2), - JavaUtil.convertJavaToRuby(runtime, t3)}; - return (R) proc.call(context, array); - } - - @Override - @SuppressWarnings("unchecked") - public R call(T1 t1, T2 t2, T3 t3, T4 t4) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), - JavaUtil.convertJavaToRuby(runtime, t2), - JavaUtil.convertJavaToRuby(runtime, t3), - JavaUtil.convertJavaToRuby(runtime, t4)}; - return (R) proc.call(context, array); - } - - @Override - @SuppressWarnings("unchecked") - public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), - JavaUtil.convertJavaToRuby(runtime, t2), - JavaUtil.convertJavaToRuby(runtime, t3), - JavaUtil.convertJavaToRuby(runtime, t4), - JavaUtil.convertJavaToRuby(runtime, t5)}; - return (R) proc.call(context, array); - } - - @Override - @SuppressWarnings("unchecked") - public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), - JavaUtil.convertJavaToRuby(runtime, t2), - JavaUtil.convertJavaToRuby(runtime, t3), - JavaUtil.convertJavaToRuby(runtime, t4), - JavaUtil.convertJavaToRuby(runtime, t5), - JavaUtil.convertJavaToRuby(runtime, t6)}; - return (R) proc.call(context, array); - } - - @Override - @SuppressWarnings("unchecked") - public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), - JavaUtil.convertJavaToRuby(runtime, t2), - JavaUtil.convertJavaToRuby(runtime, t3), - JavaUtil.convertJavaToRuby(runtime, t4), - JavaUtil.convertJavaToRuby(runtime, t5), - JavaUtil.convertJavaToRuby(runtime, t6), - JavaUtil.convertJavaToRuby(runtime, t7)}; - return (R) proc.call(context, array); - } - - @Override - @SuppressWarnings("unchecked") - public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), - JavaUtil.convertJavaToRuby(runtime, t2), - JavaUtil.convertJavaToRuby(runtime, t3), - JavaUtil.convertJavaToRuby(runtime, t4), - JavaUtil.convertJavaToRuby(runtime, t5), - JavaUtil.convertJavaToRuby(runtime, t6), - JavaUtil.convertJavaToRuby(runtime, t7), - JavaUtil.convertJavaToRuby(runtime, t8)}; - return (R) proc.call(context, array); - } - - @Override - @SuppressWarnings("unchecked") - public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) { - IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1), - JavaUtil.convertJavaToRuby(runtime, t2), - JavaUtil.convertJavaToRuby(runtime, t3), - JavaUtil.convertJavaToRuby(runtime, t4), - JavaUtil.convertJavaToRuby(runtime, t5), - JavaUtil.convertJavaToRuby(runtime, t6), - JavaUtil.convertJavaToRuby(runtime, t7), - JavaUtil.convertJavaToRuby(runtime, t8), - JavaUtil.convertJavaToRuby(runtime, t9)}; - return (R) proc.call(context, array); - } - - @Override - @SuppressWarnings("unchecked") - public R call(Object... args) { - IRubyObject[] array = new IRubyObject[args.length]; - for (int i = 0; i < args.length; i++) { - array[i] = JavaUtil.convertJavaToRuby(runtime, args[i]); - } - return (R) proc.call(context, array); - } -} diff --git a/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb b/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb deleted file mode 100644 index a080639728..0000000000 --- a/language-adaptors/rxjava-jruby/src/main/resources/rx/lang/jruby/interop.rb +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright 2013 Mike Ragalie -# -# 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. - -module Rx - module Lang - module Jruby - class Interop - WRAPPERS = { - Java::RxUtilFunctions::Action => Java::RxLangJruby::JRubyActionWrapper, - Java::Rx::Observable::OnSubscribeFunc => false - } - - WRAPPERS.default = Java::RxLangJruby::JRubyFunctionWrapper - - KLASSES = [Java::Rx::Observable, Java::RxObservables::BlockingObservable] - FUNCTION = Java::RxUtilFunctions::Function.java_class - RUNTIME = JRuby.runtime - - def self.instance - @instance ||= new - end - - def initialize - KLASSES.each do |klass| - function_methods = (klass.java_class.declared_instance_methods + klass.java_class.declared_class_methods).select do |method| - method.public? && method.parameter_types.any? {|type| FUNCTION.assignable_from?(type)} - end - - parameter_types = {} - function_methods.each do |method| - parameter_types[[method.name, method.static?]] ||= [] - - method.parameter_types.each_with_index do |type, idx| - next unless FUNCTION.assignable_from?(type) - - constructor = WRAPPERS.find do |java_class, wrapper| - type.ruby_class.ancestors.include?(java_class) - end - - # Skip OnSubscribeFuncs - next if constructor && constructor.last == false - - constructor = (constructor && constructor.last) || WRAPPERS.default - - parameter_types[[method.name, method.static?]][idx] ||= [] - parameter_types[[method.name, method.static?]][idx] << constructor - end - end - - parameter_types.each_pair do |_, types| - types.map! do |type| - next type.first if type && type.uniq.length == 1 - nil - end - end - - parameter_types.each_pair do |(method_name, static), types| - next if types.all?(&:nil?) - - klass_to_open = static ? klass.singleton_class : klass - - [method_name, underscore(method_name)].uniq.each do |name| - klass_to_open.send(:alias_method, "#{name}_without_wrapping", name) - klass_to_open.send(:define_method, name) do |*args, &block| - context = RUNTIME.get_current_context - - args = args.each_with_index.map do |arg, idx| - if arg.is_a?(Proc) && types[idx] - types[idx].new(context, arg) - else - arg - end - end - - if block && types[args.length] - block = types[args.length].new(context, block) - end - - send("#{name}_without_wrapping", *(args + [block].compact)) - end - end - end - end - end - - private - - # File activesupport/lib/active_support/inflector/methods.rb, line 89 - def underscore(camel_cased_word) - word = camel_cased_word.to_s.dup - word.gsub!('::', '/') - word.gsub!(/(?:([A-Za-z\d])|^)((?=a)b)(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" } - word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2') - word.gsub!(/([a-z\d])([A-Z])/,'\1_\2') - word.tr!("-", "_") - word.downcase! - word - end - end - end - end -end - -Rx::Lang::Jruby::Interop.instance diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb deleted file mode 100644 index 222caf34ed..0000000000 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/interop_spec.rb +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2013 Mike Ragalie -# -# 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. - -require_relative "spec_helper" - -describe Rx::Lang::Jruby::Interop do - subject { described_class.instance } - - let(:observable) { Java::Rx::Observable.from([1, 2, 3]) } - - context "with a normal, non-function method signature" do - it "calls straight through to the original Java method" do - observable.should_not_receive(:toBlockingObservable_without_wrapping) - observable.toBlockingObservable.should be_a(Java::RxObservables::BlockingObservable) - end - end - - context "with a method with a function method signature" do - it "wraps function arguments if they're in the right position" do - observable.should_receive(:subscribe_without_wrapping).with(kind_of(Java::RxLangJruby::JRubyActionWrapper)) - observable.subscribe(lambda {}) - end - - it "doesn't wrap function arguments if they're in the wrong position" do - proc = lambda {} - observable.should_receive(:subscribe_without_wrapping).with(1, 1, 1, proc) - observable.subscribe(1, 1, 1, proc) - end - - it "doesn't wrap non-function arguments" do - observable.should_receive(:subscribe_without_wrapping).with(1) - observable.subscribe(1) - end - - it "doesn't wrap OnSubscribeFunc arguments" do - proc = lambda {|observer| observer.onNext("hi")} - Java::Rx::Observable.__persistent__ = true - Java::Rx::Observable.should_not_receive(:create_without_wrapping) - Java::Rx::Observable.create(proc).should be_a(Java::Rx::Observable) - end - - it "works with underscoreized method names" do - observable. - should_receive(:finally_do_without_wrapping). - with(kind_of(Java::RxLangJruby::JRubyActionWrapper)). - and_call_original - - observable.finally_do(lambda {}) - end - - it "passes a block through as the last argument" do - proc = lambda {} - observable.should_receive(:subscribe_without_wrapping).with(1, 1, 1, 1, proc) - observable.subscribe(1, 1, 1, 1, &proc) # intentionally bogus call so it doesn't wrap the proc - end - end -end diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb deleted file mode 100644 index dae1973625..0000000000 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_action_wrapper_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2013 Mike Ragalie -# -# 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. - -require_relative "spec_helper" - -describe Java::RxLangJruby::JRubyActionWrapper do - let(:spy) { double(:spy, :call => nil) } - subject { described_class.new(JRuby.runtime.get_current_context, lambda {|*args| spy.call(args)}) } - - let(:interfaces) do - [Java::RxUtilFunctions::Action, - Java::RxUtilFunctions::Action0, - Java::RxUtilFunctions::Action1, - Java::RxUtilFunctions::Action2, - Java::RxUtilFunctions::Action3] - end - - it "implements the interfaces" do - interfaces.each do |interface| - subject.is_a?(interface) - end - end - - it "successfully uses the interfaces" do - spy.should_receive(:call).with([]) - spy.should_receive(:call).with([1]) - spy.should_receive(:call).with([1, 2]) - spy.should_receive(:call).with([1, 2, 3]) - - subject.call - subject.call(1) - subject.call(1, 2) - subject.call(1, 2, 3) - end -end diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb deleted file mode 100644 index 4c25f6ec1c..0000000000 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/jruby_function_wrapper_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright 2013 Mike Ragalie -# -# 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. - -require_relative "spec_helper" - -describe Java::RxLangJruby::JRubyFunctionWrapper do - let(:spy) { double(:spy, :call => nil) } - subject { described_class.new(JRuby.runtime.get_current_context, lambda {|*args| spy.call(args); args}) } - - let(:interfaces) do - [Java::RxUtilFunctions::Func0, - Java::RxUtilFunctions::Func1, - Java::RxUtilFunctions::Func2, - Java::RxUtilFunctions::Func3, - Java::RxUtilFunctions::Func4, - Java::RxUtilFunctions::Func5, - Java::RxUtilFunctions::Func6, - Java::RxUtilFunctions::Func7, - Java::RxUtilFunctions::Func8, - Java::RxUtilFunctions::Func9, - Java::RxUtilFunctions::FuncN] - end - - it "implements the interfaces" do - interfaces.each do |interface| - subject.is_a?(interface) - end - end - - it "successfully uses the interfaces" do - spy.should_receive(:call).with([]) - spy.should_receive(:call).with([1]) - spy.should_receive(:call).with([1, 2]) - spy.should_receive(:call).with([1, 2, 3]) - spy.should_receive(:call).with([1, 2, 3, 4]) - spy.should_receive(:call).with([1, 2, 3, 4, 5]) - spy.should_receive(:call).with([1, 2, 3, 4, 5, 6]) - spy.should_receive(:call).with([1, 2, 3, 4, 5, 6, 7]) - spy.should_receive(:call).with([1, 2, 3, 4, 5, 6, 7, 8]) - spy.should_receive(:call).with([1, 2, 3, 4, 5, 6, 7, 8, 9]) - spy.should_receive(:call).with([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) - - subject.call.should == [] - subject.call(1).should == [1] - subject.call(1, 2).should == [1, 2] - subject.call(1, 2, 3).should == [1, 2, 3] - subject.call(1, 2, 3, 4).should == [1, 2, 3, 4] - subject.call(1, 2, 3, 4, 5).should == [1, 2, 3, 4, 5] - subject.call(1, 2, 3, 4, 5, 6).should == [1, 2, 3, 4, 5, 6] - subject.call(1, 2, 3, 4, 5, 6, 7).should == [1, 2, 3, 4, 5, 6, 7] - subject.call(1, 2, 3, 4, 5, 6, 7, 8).should == [1, 2, 3, 4, 5, 6, 7, 8] - subject.call(1, 2, 3, 4, 5, 6, 7, 8, 9).should == [1, 2, 3, 4, 5, 6, 7, 8, 9] - subject.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).should == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - end -end diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb deleted file mode 100644 index fc9cde1b0b..0000000000 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/performance.rb +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright 2013 Mike Ragalie -# -# 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. - -# Current execution times: -# Without rxjava-jruby: 3.31s -# With rxjava-jruby: 2.18s - -require 'optparse' - -options = {} -OptionParser.new do |opts| - opts.banner = "Usage: jruby --profile.api performance.rb [options]" - - opts.on("-c", "--core CORE-PATH", "Path to the rxjava-core.jar") {|core| options[:core] = core} - opts.on("-j", "--jruby [JRUBY-PATH]", "Path to the rxjava-jruby.jar (optional)") {|jruby| options[:jruby] = jruby} -end.parse! - -require options[:core] - -if options[:jruby] - require options[:jruby] - require 'rx/lang/jruby/interop' -end - -require 'jruby/profiler' - -profile_data = JRuby::Profiler.profile do - 10000.times do - o = Java::Rx::Observable.create do |observer| - observer.onNext("one") - observer.onNext("two") - observer.onNext("three") - observer.onCompleted - Java::RxSubscriptions::Subscription.empty - end - o.map {|n| n * 2}.subscribe {|n| n} - end -end - -profile_printer = JRuby::Profiler::FlatProfilePrinter.new(profile_data) -profile_printer.printProfile(STDOUT) diff --git a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb b/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb deleted file mode 100644 index 99b095960f..0000000000 --- a/language-adaptors/rxjava-jruby/src/spec/ruby/rx/lang/jruby/spec_helper.rb +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2013 Mike Ragalie -# -# 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. - -require "rx/lang/jruby/interop" diff --git a/language-adaptors/rxjava-kotlin/README.md b/language-adaptors/rxjava-kotlin/README.md deleted file mode 100644 index 037d759654..0000000000 --- a/language-adaptors/rxjava-kotlin/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Kotlin Adaptor for RxJava - -Kotlin has support for SAM (Single Abstract Method) Interfaces as Functions (i.e. Java 8 Lambdas). So you could use Kotlin in RxJava whitout this adaptor - -```kotlin -Observable.create(OnSubscribeFunc<String> { observer -> - observer!!.onNext("Hello") - observer.onCompleted() - Subscriptions.empty() -})!!.subscribe { result -> - a!!.received(result) -} -``` - -In RxJava [0.17.0](https://github.com/Netflix/RxJava/releases/tag/0.17.0) version a new Subscriber type was included - -```kotlin -Observable.create(object:OnSubscribe<String> { - override fun call(subscriber: Subscriber<in String>?) { - subscriber!!.onNext("Hello") - subscriber.onCompleted() - } -})!!.subscribe { result -> - a!!.received(result) -} -``` - -(Due to a [bug in Kotlin's compiler](http://youtrack.jetbrains.com/issue/KT-4753) you can't use SAM with OnSubscribe) - -This adaptor exposes a set of Extension functions that allow a more idiomatic Kotlin usage - -```kotlin -{(subscriber: Subscriber<in String>) -> - subscriber.onNext("Hello") - subscriber.onCompleted() -}.asObservable().subscribe { result -> - a!!.received(result) -} -``` - -## 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-kotlin%22). - -Example for Maven: - -```xml -<dependency> - <groupId>com.netflix.rxjava</groupId> - <artifactId>rxjava-kotlin</artifactId> - <version>x.y.z</version> -</dependency> -``` - -and for Ivy: - -```xml -<dependency org="com.netflix.rxjava" name="rxjava-kotlin" rev="x.y.z" /> -``` - -and for Gradle: - -```groovy -compile 'com.netflix.rxjava:rxjava-kotlin:x.y.z' -``` \ No newline at end of file diff --git a/language-adaptors/rxjava-kotlin/build.gradle b/language-adaptors/rxjava-kotlin/build.gradle deleted file mode 100644 index e64b9e3801..0000000000 --- a/language-adaptors/rxjava-kotlin/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -buildscript { - repositories() { - mavenCentral() - } - - dependencies { - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.8.11' - } -} - -apply plugin: 'kotlin' -apply plugin: 'osgi' - -dependencies { - compile project(':rxjava-core') - compile 'org.jetbrains.kotlin:kotlin-stdlib:0.8.11' - testCompile 'junit:junit-dep:4.10' - testCompile 'org.mockito:mockito-core:1.8.5' -} - -jar { - manifest { - name = 'rxjava-kotlin' - 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' - } -} diff --git a/language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt b/language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt deleted file mode 100644 index 006f97e48a..0000000000 --- a/language-adaptors/rxjava-kotlin/src/main/kotlin/rx/lang/kotlin/namespace.kt +++ /dev/null @@ -1,81 +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.kotlin - -import rx.Observer -import rx.Observable -import rx.Observable.OnSubscribe -import rx.Subscription -import rx.Observable.OnSubscribeFunc -import rx.Subscriber - - -public fun<T> Function1<Subscriber<in T>, Unit>.asObservable(): Observable<T> { - return Observable.create(object:OnSubscribe<T> { - override fun call(t1: Subscriber<in T>?) { - this@asObservable(t1!!) - } - - })!! -} - -[deprecated("Use Function1<Subscriber<in T>, Unit>.asObservable()")] -public fun<T> Function1<Observer<in T>, Subscription>.asObservableFunc(): Observable<T> { - return Observable.create(OnSubscribeFunc<T>{ op -> - this(op!!) - })!! -} - -public fun<T> Function0<Observable<out T>>.defer(): Observable<T> { - return Observable.defer(this)!! -} - -public fun<T> Iterable<T>.asObservable(): Observable<T> { - return Observable.from(this)!! -} - -public fun<T> T.asObservable(): Observable<T> { - return Observable.from(this)!! -} - -public fun<T> Throwable.asObservable(): Observable<T> { - return Observable.error(this)!! -} - -public fun<T> Pair<T, T>.asObservable(): Observable<T> { - return Observable.from(listOf(this.component1(), this.component2()))!! -} - -public fun<T> Triple<T, T, T>.asObservable(): Observable<T> { - return Observable.from(listOf(this.component1(), this.component2(), this.component3()))!! -} - -public fun<T> Pair<Observable<T>, Observable<T>>.merge(): Observable<T> { - return Observable.merge(this.component1(), this.component2())!! -} - -public fun<T> Triple<Observable<T>, Observable<T>, Observable<T>>.merge(): Observable<T> { - return Observable.merge(this.component1(), this.component2(), this.component3())!! -} - -public fun<T> Pair<Observable<T>, Observable<T>>.mergeDelayError(): Observable<T> { - return Observable.mergeDelayError(this.component1(), this.component2())!! -} - -public fun<T> Triple<Observable<T>, Observable<T>, Observable<T>>.mergeDelayError(): Observable<T> { - return Observable.mergeDelayError(this.component1(), this.component2(), this.component3())!! -} diff --git a/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt b/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt deleted file mode 100644 index 8cc6cf8411..0000000000 --- a/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/BasicKotlinTests.kt +++ /dev/null @@ -1,348 +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.kotlin - -import rx.Observable -import org.junit.Test -import org.mockito.Mockito.* -import org.mockito.Matchers.* -import org.junit.Assert.* -import rx.Notification -import kotlin.concurrent.thread -import rx.lang.kotlin.BasicKotlinTests.AsyncObservable -import rx.Observable.OnSubscribe -import rx.Subscriber - -/** - * This class use plain Kotlin without extensions from the language adaptor - */ -public class BasicKotlinTests : KotlinTests() { - - - [Test] - public fun testCreate() { - - Observable.create(object:OnSubscribe<String> { - override fun call(subscriber: Subscriber<in String>?) { - subscriber!!.onNext("Hello") - subscriber.onCompleted() - } - })!!.subscribe { result -> - a!!.received(result) - } - - verify(a, times(1))!!.received("Hello") - } - - [Test] - public fun testFilter() { - Observable.from(listOf(1, 2, 3))!!.filter { it!! >= 2 }!!.subscribe(received()) - verify(a, times(0))!!.received(1); - verify(a, times(1))!!.received(2); - verify(a, times(1))!!.received(3); - } - - [Test] - public fun testLast() { - assertEquals("three", Observable.from(listOf("one", "two", "three"))!!.toBlocking()!!.last()) - } - - [Test] - public fun testLastWithPredicate() { - assertEquals("two", Observable.from(listOf("one", "two", "three"))!!.toBlocking()!!.last { x -> x!!.length == 3 }) - } - - [Test] - public fun testMap1() { - Observable.from(1)!!.map { v -> "hello_$v" }!!.subscribe(received()) - verify(a, times(1))!!.received("hello_1") - } - - [Test] - public fun testMap2() { - Observable.from(listOf(1, 2, 3))!!.map { v -> "hello_$v" }!!.subscribe((received())) - verify(a, times(1))!!.received("hello_1") - verify(a, times(1))!!.received("hello_2") - verify(a, times(1))!!.received("hello_3") - } - - [Test] - public fun testMaterialize() { - Observable.from(listOf(1, 2, 3))!!.materialize()!!.subscribe((received())) - verify(a, times(4))!!.received(any(javaClass<Notification<Int>>())) - verify(a, times(0))!!.error(any(javaClass<Exception>())) - } - - [Test] - public fun testMergeDelayError() { - Observable.mergeDelayError( - Observable.from(listOf(1, 2, 3)), - Observable.merge( - Observable.from(6), - Observable.error(NullPointerException()), - Observable.from(7) - ), - Observable.from(listOf(4, 5)) - )!!.subscribe(received(), { e -> a!!.error(e) }) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(1))!!.received(3) - verify(a, times(1))!!.received(4) - verify(a, times(1))!!.received(5) - verify(a, times(1))!!.received(6) - verify(a, times(0))!!.received(7) - verify(a, times(1))!!.error(any(javaClass<NullPointerException>())) - } - - [Test] - public fun testMerge() { - Observable.merge( - Observable.from(listOf(1, 2, 3)), - Observable.merge( - Observable.from(6), - Observable.error(NullPointerException()), - Observable.from(7) - ), - Observable.from(listOf(4, 5)) - )!!.subscribe(received(), { e -> a!!.error(e) }) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(1))!!.received(3) - verify(a, times(0))!!.received(4) - verify(a, times(0))!!.received(5) - verify(a, times(1))!!.received(6) - verify(a, times(0))!!.received(7) - verify(a, times(1))!!.error(any(javaClass<NullPointerException>())) - } - - [Test] - public fun testScriptWithMaterialize() { - TestFactory().observable.materialize()!!.subscribe((received())) - verify(a, times(2))!!.received(any(javaClass<Notification<Int>>())) - } - - [Test] - public fun testScriptWithMerge() { - val factory = TestFactory() - Observable.merge(factory.observable, factory.observable)!!.subscribe((received())) - verify(a, times(1))!!.received("hello_1") - verify(a, times(1))!!.received("hello_2") - } - - [Test] - public fun testFromWithIterable() { - val list = listOf(1, 2, 3, 4, 5) - assertEquals(5, Observable.from(list)!!.count()!!.toBlocking()!!.single()) - } - - [Test] - public fun testFromWithObjects() { - val list = listOf(1, 2, 3, 4, 5) - assertEquals(2, Observable.from(listOf(list, 6))!!.count()!!.toBlocking()!!.single()) - } - - [Test] - public fun testStartWith() { - val list = listOf(10, 11, 12, 13, 14) - val startList = listOf(1, 2, 3, 4, 5) - assertEquals(6, Observable.from(list)!!.startWith(0)!!.count()!!.toBlocking()!!.single()) - assertEquals(10, Observable.from(list)!!.startWith(startList)!!.count()!!.toBlocking()!!.single()) - } - - [Test] - public fun testScriptWithOnNext() { - TestFactory().observable.subscribe((received())) - verify(a, times(1))!!.received("hello_1") - } - - [Test] - public fun testSkipTake() { - Observable.from(listOf(1, 2, 3))!!.skip(1)!!.take(1)!!.subscribe(received()) - verify(a, times(0))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(0))!!.received(3) - } - - [Test] - public fun testSkip() { - Observable.from(listOf(1, 2, 3))!!.skip(2)!!.subscribe(received()) - verify(a, times(0))!!.received(1) - verify(a, times(0))!!.received(2) - verify(a, times(1))!!.received(3) - } - - [Test] - public fun testTake() { - Observable.from(listOf(1, 2, 3))!!.take(2)!!.subscribe(received()) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(0))!!.received(3) - } - - [Test] - public fun testTakeLast() { - TestFactory().observable.takeLast(1)!!.subscribe((received())) - verify(a, times(1))!!.received("hello_1") - } - - [Test] - public fun testTakeWhile() { - Observable.from(listOf(1, 2, 3))!!.takeWhile { x -> x!! < 3 }!!.subscribe(received()) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(0))!!.received(3) - } - - [Test] - public fun testTakeWhileWithIndex() { - Observable.from(listOf(1, 2, 3))!!.takeWhileWithIndex { x, i -> i!! < 2 }!!.subscribe(received()) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(0))!!.received(3) - } - - [Test] - public fun testToSortedList() { - TestFactory().numbers.toSortedList()!!.subscribe(received()) - verify(a, times(1))!!.received(listOf(1, 2, 3, 4, 5)) - } - - [Test] - public fun testForEach() { - Observable.create(AsyncObservable())!!.toBlocking()!!.forEach(received()) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(1))!!.received(3) - } - - [Test(expected = javaClass<RuntimeException>())] - public fun testForEachWithError() { - Observable.create(AsyncObservable())!!.toBlocking()!!.forEach { throw RuntimeException("err") } - fail("we expect an exception to be thrown") - } - - [Test] - public fun testLastOrDefault() { - assertEquals("two", Observable.from(listOf("one", "two"))!!.toBlocking()!!.lastOrDefault("default") { x -> x!!.length == 3 }) - assertEquals("default", Observable.from(listOf("one", "two"))!!.toBlocking()!!.lastOrDefault("default") { x -> x!!.length > 3 }) - } - - [Test(expected = javaClass<IllegalArgumentException>())] - public fun testSingle() { - assertEquals("one", Observable.from("one")!!.toBlocking()!!.single { x -> x!!.length == 3 }) - Observable.from(listOf("one", "two"))!!.toBlocking()!!.single { x -> x!!.length == 3 } - fail() - } - - [Test] - public fun testDefer() { - Observable.defer { Observable.from(listOf(1, 2)) }!!.subscribe(received()) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - } - - [Test] - public fun testAll() { - Observable.from(listOf(1, 2, 3))!!.all { x -> x!! > 0 }!!.subscribe(received()) - verify(a, times(1))!!.received(true) - } - - [Test] - public fun testZip() { - val o1 = Observable.from(listOf(1, 2, 3))!! - val o2 = Observable.from(listOf(4, 5, 6))!! - val o3 = Observable.from(listOf(7, 8, 9))!! - - val values = Observable.zip(o1, o2, o3) { a, b, c -> listOf(a, b, c) }!!.toList()!!.toBlocking()!!.single()!! - assertEquals(listOf(1, 4, 7), values[0]) - assertEquals(listOf(2, 5, 8), values[1]) - assertEquals(listOf(3, 6, 9), values[2]) - } - - [Test] - public fun testZipWithIterable() { - val o1 = Observable.from(listOf(1, 2, 3))!! - val o2 = Observable.from(listOf(4, 5, 6))!! - val o3 = Observable.from(listOf(7, 8, 9))!! - - val values = Observable.zip(listOf(o1, o2, o3)) { args -> listOf(*args) }!!.toList()!!.toBlocking()!!.single()!! - assertEquals(listOf(1, 4, 7), values[0]) - assertEquals(listOf(2, 5, 8), values[1]) - assertEquals(listOf(3, 6, 9), values[2]) - } - - [Test] - public fun testGroupBy() { - var count = 0 - - Observable.from(listOf("one", "two", "three", "four", "five", "six"))!! - .groupBy { s -> s!!.length }!! - .flatMap { groupObervable -> - groupObervable!!.map { s -> - "Value: $s Group ${groupObervable.getKey()}" - } - }!!.toBlocking()!!.forEach { s -> - println(s) - count++ - } - - assertEquals(6, count) - } - - - - public class TestFactory() { - var counter = 1 - - val numbers: Observable<Int> - get(){ - return Observable.from(listOf(1, 3, 2, 5, 4))!! - } - - val onSubscribe: TestOnSubscribe - get(){ - return TestOnSubscribe(counter++) - } - - val observable: Observable<String> - get(){ - return Observable.create(onSubscribe)!! - } - - } - - class AsyncObservable : OnSubscribe<Int> { - override fun call(op: Subscriber<in Int>?) { - thread { - Thread.sleep(50) - op!!.onNext(1) - op.onNext(2) - op.onNext(3) - op.onCompleted() - } - - } - } - - class TestOnSubscribe(val count: Int) : OnSubscribe<String> { - override fun call(op: Subscriber<in String>?) { - op!!.onNext("hello_$count") - op.onCompleted() - } - - } -} \ No newline at end of file diff --git a/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt b/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt deleted file mode 100644 index 19ce653df9..0000000000 --- a/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/ExtensionTests.kt +++ /dev/null @@ -1,297 +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.kotlin - -import rx.Observable -import org.junit.Test -import org.mockito.Mockito.* -import org.mockito.Matchers.* -import org.junit.Assert.* -import rx.Notification -import kotlin.concurrent.thread -import rx.Subscriber - -/** - * This class contains tests using the extension functions provided by the language adaptor. - */ -public class ExtensionTests : KotlinTests() { - - - [Test] - public fun testCreate() { - - {(subscriber: Subscriber<in String>) -> - subscriber.onNext("Hello") - subscriber.onCompleted() - }.asObservable().subscribe { result -> - a!!.received(result) - } - - verify(a, times(1))!!.received("Hello") - } - - [Test] - public fun testFilter() { - listOf(1, 2, 3).asObservable().filter { it!! >= 2 }!!.subscribe(received()) - verify(a, times(0))!!.received(1); - verify(a, times(1))!!.received(2); - verify(a, times(1))!!.received(3); - } - - - [Test] - public fun testLast() { - assertEquals("three", listOf("one", "two", "three").asObservable().toBlocking()!!.last()) - } - - [Test] - public fun testLastWithPredicate() { - assertEquals("two", listOf("one", "two", "three").asObservable().toBlocking()!!.last { x -> x!!.length == 3 }) - } - - [Test] - public fun testMap1() { - 1.asObservable().map { v -> "hello_$v" }!!.subscribe((received())) - verify(a, times(1))!!.received("hello_1") - } - - [Test] - public fun testMap2() { - listOf(1, 2, 3).asObservable().map { v -> "hello_$v" }!!.subscribe((received())) - verify(a, times(1))!!.received("hello_1") - verify(a, times(1))!!.received("hello_2") - verify(a, times(1))!!.received("hello_3") - } - - [Test] - public fun testMaterialize() { - listOf(1, 2, 3).asObservable().materialize()!!.subscribe((received())) - verify(a, times(4))!!.received(any(javaClass<Notification<Int>>())) - verify(a, times(0))!!.error(any(javaClass<Exception>())) - } - - [Test] - public fun testMergeDelayError() { - Triple(listOf(1, 2, 3).asObservable(), - Triple(6.asObservable(), - NullPointerException().asObservable<Int>(), - 7.asObservable() - ).merge(), - listOf(4, 5).asObservable() - ).mergeDelayError().subscribe(received(), { e -> a!!.error(e) }) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(1))!!.received(3) - verify(a, times(1))!!.received(4) - verify(a, times(1))!!.received(5) - verify(a, times(1))!!.received(6) - verify(a, times(0))!!.received(7) - verify(a, times(1))!!.error(any(javaClass<NullPointerException>())) - } - - [Test] - public fun testMerge() { - Triple(listOf(1, 2, 3).asObservable(), - Triple(6.asObservable(), - NullPointerException().asObservable<Int>(), - 7.asObservable() - ).merge(), - listOf(4, 5).asObservable() - ).merge().subscribe(received(), { e -> a!!.error(e) }) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(1))!!.received(3) - verify(a, times(0))!!.received(4) - verify(a, times(0))!!.received(5) - verify(a, times(1))!!.received(6) - verify(a, times(0))!!.received(7) - verify(a, times(1))!!.error(any(javaClass<NullPointerException>())) - } - - [Test] - public fun testScriptWithMaterialize() { - TestFactory().observable.materialize()!!.subscribe((received())) - verify(a, times(2))!!.received(any(javaClass<Notification<Int>>())) - } - - [Test] - public fun testScriptWithMerge() { - val factory = TestFactory() - (factory.observable to factory.observable).merge().subscribe((received())) - verify(a, times(1))!!.received("hello_1") - verify(a, times(1))!!.received("hello_2") - } - - - [Test] - public fun testFromWithIterable() { - assertEquals(5, listOf(1, 2, 3, 4, 5).asObservable().count()!!.toBlocking()!!.single()) - } - - [Test] - public fun testStartWith() { - val list = listOf(10, 11, 12, 13, 14) - val startList = listOf(1, 2, 3, 4, 5) - assertEquals(6, list.asObservable().startWith(0)!!.count()!!.toBlocking()!!.single()) - assertEquals(10, list.asObservable().startWith(startList)!!.count()!!.toBlocking()!!.single()) - } - - [Test] - public fun testScriptWithOnNext() { - TestFactory().observable.subscribe((received())) - verify(a, times(1))!!.received("hello_1") - } - - [Test] - public fun testSkipTake() { - Triple(1, 2, 3).asObservable().skip(1)!!.take(1)!!.subscribe(received()) - verify(a, times(0))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(0))!!.received(3) - } - - [Test] - public fun testSkip() { - Triple(1, 2, 3).asObservable().skip(2)!!.subscribe(received()) - verify(a, times(0))!!.received(1) - verify(a, times(0))!!.received(2) - verify(a, times(1))!!.received(3) - } - - [Test] - public fun testTake() { - Triple(1, 2, 3).asObservable().take(2)!!.subscribe(received()) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(0))!!.received(3) - } - - [Test] - public fun testTakeLast() { - TestFactory().observable.takeLast(1)!!.subscribe((received())) - verify(a, times(1))!!.received("hello_1") - } - - [Test] - public fun testTakeWhile() { - Triple(1, 2, 3).asObservable().takeWhile { x -> x!! < 3 }!!.subscribe(received()) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(0))!!.received(3) - } - - [Test] - public fun testTakeWhileWithIndex() { - Triple(1, 2, 3).asObservable().takeWhileWithIndex { x, i -> i!! < 2 }!!.subscribe(received()) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(0))!!.received(3) - } - - [Test] - public fun testToSortedList() { - TestFactory().numbers.toSortedList()!!.subscribe(received()) - verify(a, times(1))!!.received(listOf(1, 2, 3, 4, 5)) - } - - [Test] - public fun testForEach() { - asyncObservable.asObservable().toBlocking()!!.forEach(received()) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - verify(a, times(1))!!.received(3) - } - - [Test(expected = javaClass<RuntimeException>())] - public fun testForEachWithError() { - asyncObservable.asObservable().toBlocking()!!.forEach { throw RuntimeException("err") } - fail("we expect an exception to be thrown") - } - - [Test] - public fun testLastOrDefault() { - assertEquals("two", ("one" to"two").asObservable().toBlocking()!!.lastOrDefault("default") { x -> x!!.length == 3 }) - assertEquals("default", ("one" to"two").asObservable().toBlocking()!!.lastOrDefault("default") { x -> x!!.length > 3 }) - } - - [Test] - public fun testDefer() { - { (1 to 2).asObservable() }.defer().subscribe(received()) - verify(a, times(1))!!.received(1) - verify(a, times(1))!!.received(2) - } - - [Test] - public fun testAll() { - Triple(1, 2, 3).asObservable().all { x -> x!! > 0 }!!.subscribe(received()) - verify(a, times(1))!!.received(true) - } - - [Test] - public fun testZip() { - val o1 = Triple(1, 2, 3).asObservable() - val o2 = Triple(4, 5, 6).asObservable() - val o3 = Triple(7, 8, 9).asObservable() - - val values = Observable.zip(o1, o2, o3) { a, b, c -> listOf(a, b, c) }!!.toList()!!.toBlocking()!!.single()!! - assertEquals(listOf(1, 4, 7), values[0]) - assertEquals(listOf(2, 5, 8), values[1]) - assertEquals(listOf(3, 6, 9), values[2]) - } - - val funOnSubscribe: (Int, Subscriber<in String>) -> Unit = { counter, subscriber -> - subscriber.onNext("hello_$counter") - subscriber.onCompleted() - } - - val asyncObservable: (Subscriber<in Int>) -> Unit = { subscriber -> - thread { - Thread.sleep(50) - subscriber.onNext(1) - subscriber.onNext(2) - subscriber.onNext(3) - subscriber.onCompleted() - } - } - - /** - * Copied from (funKTionale)[https://github.com/MarioAriasC/funKTionale/blob/master/src/main/kotlin/org/funktionale/partials/namespace.kt] - */ - public fun <P1, P2, R> Function2<P1, P2, R>.partially1(p1: P1): (P2) -> R { - return {(p2: P2) -> this(p1, p2) } - } - - inner public class TestFactory() { - var counter = 1 - - val numbers: Observable<Int> - get(){ - return listOf(1, 3, 2, 5, 4).asObservable() - } - - val onSubscribe: (Subscriber<in String>) -> Unit - get(){ - return funOnSubscribe.partially1(counter++) - } - - val observable: Observable<String> - get(){ - return onSubscribe.asObservable() - } - - } -} \ No newline at end of file diff --git a/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt b/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt deleted file mode 100644 index 59827c7b5c..0000000000 --- a/language-adaptors/rxjava-kotlin/src/test/kotlin/rx/lang/kotlin/KotlinTests.kt +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package rx.lang.kotlin - -import org.mockito.MockitoAnnotations -import org.junit.Before -import rx.Observable -import org.mockito.Mock - -public abstract class KotlinTests { - [Mock] var a: ScriptAssertion? = null - [Mock] var w: Observable<Int>? = null - - [Before] - public fun before() { - MockitoAnnotations.initMocks(this) - } - - fun received<T>(): (T?) -> Unit { - return {(result: T?) -> a!!.received(result) } - } - - public trait ScriptAssertion{ - fun error(e: Throwable?) - - fun received(e: Any?) - } -} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/README.md b/language-adaptors/rxjava-scala/README.md deleted file mode 100644 index fb56b94eb1..0000000000 --- a/language-adaptors/rxjava-scala/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# Scala Adaptor for RxJava - -This adaptor allows to use RxJava in Scala with anonymous functions, e.g. - -```scala -val o = Observable.interval(200 millis).take(5) -o.subscribe(n => println("n = " + n)) -Observable.items(1, 2, 3, 4).reduce(_ + _) -``` - -For-comprehensions are also supported: - -```scala -val first = Observable.items(10, 11, 12) -val second = Observable.items(10, 11, 12) -val booleans = for ((n1, n2) <- (first zip second)) yield (n1 == n2) -``` - -Further, this adaptor attempts to expose an API which is as Scala-idiomatic as possible. This means that certain methods have been renamed, their signature was changed, or static methods were changed to instance methods. Some examples: - -```scala - // instead of concat: -def ++[U >: T](that: Observable[U]): Observable[U] - -// instance method instead of static: -def zip[U](that: Observable[U]): Observable[(T, U)] - -// the implicit evidence argument ensures that dematerialize can only be called on Observables of Notifications: -def dematerialize[U](implicit evidence: T <:< Notification[U]): Observable[U] - -// additional type parameter U with lower bound to get covariance right: -def onErrorResumeNext[U >: T](resumeFunction: Throwable => Observable[U]): Observable[U] - -// curried in Scala collections, so curry fold also here: -def fold[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] - -// using Duration instead of (long timepan, TimeUnit duration): -def sample(duration: Duration): Observable[T] - -// called skip in Java, but drop in Scala -def drop(n: Int): Observable[T] - -// there's only mapWithIndex in Java, because Java doesn't have tuples: -def zipWithIndex: Observable[(T, Int)] - -// corresponds to Java's toList: -def toSeq: Observable[Seq[T]] - -// the implicit evidence argument ensures that switch can only be called on Observables of Observables: -def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] - -// Java's from becomes apply, and we use Scala Range -def apply(range: Range): Observable[Int] - -// use Bottom type: -def never: Observable[Nothing] -``` - -Also, the Scala Observable is fully covariant in its type parameter, whereas the Java Observable only achieves partial covariance due to limitations of Java's type system (or if you can fix this, your suggestions are very welcome). - -For more examples, see [RxScalaDemo.scala](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala). - -Scala code using Rx should only import members from `rx.lang.scala` and below. - - -## Documentation - -The API documentation can be found [here](http://rxscala.github.io/scaladoc/index.html#rx.lang.scala.Observable). - -Note that starting from version 0.15, `rx.lang.scala.Observable` is not a value class any more. [./Rationale.md](https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/Rationale.md) explains why. - -You can build the API documentation yourself by running `./gradlew scaladoc` in the RxJava root directory. - -Then navigate to `RxJava/language-adaptors/rxjava-scala/build/docs/scaladoc/index.html` to display it. - - -## 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-scala%22). - -Example for Maven: - -```xml -<dependency> - <groupId>com.netflix.rxjava</groupId> - <artifactId>rxjava-scala</artifactId> - <version>x.y.z</version> -</dependency> -``` - -and for Ivy: - -```xml -<dependency org="com.netflix.rxjava" name="rxjava-scala" rev="x.y.z" /> -``` - -and for sbt: - -```scala -libraryDependencies ++= Seq( - "com.netflix.rxjava" % "rxjava-scala" % "x.y.z" -) -``` diff --git a/language-adaptors/rxjava-scala/Rationale.md b/language-adaptors/rxjava-scala/Rationale.md deleted file mode 100644 index a8cd47d954..0000000000 --- a/language-adaptors/rxjava-scala/Rationale.md +++ /dev/null @@ -1,144 +0,0 @@ -Alternative Rx bindings for Scala -================================= - -The current RxScala binding attempt to optimize for seamless interop between Scala and Java. -The intended interop is illustrated by the following example where in Scala a class is defined that takes -an `Observable[Movie]` that is transformed using RxScala operators: -```scala -class MovieLib(val moviesStream: Observable[Movie]) { - val threshold = 1200 - def shortMovies: Observable[Movie] = ??? - def longMovies: Observable[Movie] = ??? -} -``` -which is then called in Java, passing a Java `Observable<Movie>` to the constructor -```java -public void test() { - MovieLib lib = new MovieLib(Observable.from(...)); - - lib.longMovies().subscribe(moviePrinter); -} -``` -The technique used to obtain this transparency is to use a value class with a private constructor that implements -the Rx operators in an idiomatic Scala way, and a companion object that is used to construct instances in Scala -```scala -object Observable { - def apply[T](asJava: rx.Observable[_ <: T]): Observable[T] = { new Observable[T](asJava) } -} - -class Observable[+T] private[scala] (val asJava: rx.Observable[_ <: T]) extends AnyVal { - // Idiomatic Scala friendly definitions of Rx operators -} -``` -Since `rx.lang.scala.Observable[T] extends AnyVal`, the underlying representation of `rx.lang.scala.Observable[T]` -is the same as `rx.Observable<T>`. Because `rx.lang.scala.Observable[T]` is an opaque type in Scala, -the Scala programmer only sees the Scala-friendly operators. - -However, in the current the illusion of interop is quickly lost when going beyond this simple example. -For example but type `Notification[T]` and `Scheduler[T]` are defined using wrappers, -and hence they are not compatible with `Notification<T>` respectively `Scheduler<T>`. -For instance, when materializing an `Observable[T]` in Scala to an `Observable[Notification[T]]`, -we lost the seamless interop with `Observable<Notification<T>>` on the Java side. - -However, the real problems with seamless interop show up when we try to creating bindings for other Rx types. -In particular types that have inheritance or more structure. - -For example, RxScala currently defines a type synonym `type Observer[-T] = rx.Observer[_ >: T]`, -but no further bindings for observers. -Similarly, for subjects RxScala defines `type Subject[-T, +R] = rx.subjects.Subject[_ >: T, _ <: R]`. -The problem with these definitions is that on the Java side, subjects are defined as: -```scala -public abstract class Subject<T, R> extends Observable<R> implements Observer<T> { …} -``` -without binding any of the Rx subjects. - -The consequence is that `Subject[S,T]` in Scala is unrelated to `rx.lang.scala.Observable[T]` in Scala, -but shows up as a `rx.Observable[T]`. The problem however is that if we want to expose subjects in Scala -such that they derive from both `Observable[S]` and `Observer[T]` we cannot use the `extend AnyVal` trick -we used for `Observable[T]` and immediately lose transparent interop with Java. - -The problem is even worse because `AsyncSubject<T>`, `BehaviorSubject<T>`, … all derive from `Subject<T,T>`, -so if we want them to derive from a common base `Subject[T,T]` type in Scala we lose transparency for those as well. -And again, if we expose the various subjects by extending `AnyVal`, they are useless in Scala because they do not inherit -from a common base type. To avoid implementing all methods of observable and observer on each specific subject -we might add implicit conversions to `Observable[T]` and `Observer[T]` but that still does not give Scala users -a native `Subject[S,T]` type. -```scala -object AsyncSubject { - def apply[T](): AsyncSubject[T] = - new AsyncSubject[T](rx.subjects.AsyncSubject.create()) -} - -class AsyncSubject[T] private [scala] (val inner: rx.subjects.AsyncSubject[T]) - extends AnyVal -{ … } - -implicit final def asObservable[T](subject: AsyncSubject[T]): Observable[T] = - Observable(subject.inner) - -implicit final def asObserver[T](subject: AsyncSubject[T]): Observer[T] = - subject.inner -``` -The inheritance problem is not just limited to subjects, but also surfaces for subscriptions. -Rx scala currently defines `type Subscription = rx.Subscription` using a type synonym as well, -and we run into exactly the same problems as with subjects when we try to bind the -various Rx subscriptions `BooleanSubscription`, `SerialSubscription`, etc. - -Since we cannot wrap Rx types in Scala such that they are both (a) transparently interoperable with Java, -and (b) feel native and idiomatic to Scala, we should decide in favor of optimizing RxScala for Scala -and consumption of Rx values from Java but not for Scala as a producer. - -If we take that approach, we can make bindings that feels like a completely native Scala library, -without needing any complications of the Scala side. -```scala -object Observable { …} -trait Observable[+T] { - def asJavaObservable: rx.Observable[_ <: T] -} - -object Observer {…} -trait Observer[-T] { - def asJavaObserver: rx.Observer[_ >: T] -} - -object Subject {…} -trait Subject[-T, +R] extends Observable[R] with Observer[T] { - val asJavaSubject: rx.subjects.Subject[_ >: T, _<: R] -} - -object Scheduler {…} -trait Scheduler { - def asJavaScheduler: rx.Scheduler; -} - -object Notification {…} -trait Notification[+T] { - def asJavaNotification: rx.Notification[_ <: T] -} - -object Subscription {…} -trait Subscription { - def asJavaSubscription: rx.Subscription -} -``` -You pay the price when crossing the Scala/Java interop boundary, which is where it should be. -The proper way is to put the burden of interop on the Scala side, in case you want to create -a reusable Rx-based library in Scala, or wrap and unwrap on the Java side. -```java -public static void main(String[] args) { - - Observable<Movie> movies = Observable.from(new Movie(3000), new Movie(1000), new Movie(2000)); - MovieLib lib = new MovieLib(toScalaObservable(movies)); - lib.longMovies().asJavaObservable().subscribe(m -> - System.out.println("A movie of length " + m.lengthInSeconds() + "s") - ); -} -``` -Delegation versus Inheritance ------------------------------ -The obvious thought is that using delegation instead of inheritance (http://c2.com/cgi/wiki?DelegationIsInheritance) -will lead to excessive wrapping, since all Scala types wrap and delegate to an underlying RxJava implementation. -Note however, that the wrapping happens at query generation time and incurs no overhead when messages are flowing -through the pipeline. Say we have a query `xs.map(f).filter(p).subscribe(o)`. Even though the Scala types are wrappers, -the callback that is registered with xs is something like `x => { val y = f(x); if(p(y)){ o.asJavaObserver.onNext(y) }}` -and hence there is no additional runtime penalty. \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/ReleaseNotes.md b/language-adaptors/rxjava-scala/ReleaseNotes.md deleted file mode 100644 index 4fdb3a06fd..0000000000 --- a/language-adaptors/rxjava-scala/ReleaseNotes.md +++ /dev/null @@ -1,228 +0,0 @@ -RxScala Release Notes -===================== - -This release of the RxScala bindings builds on the previous 0.15 release to make the Rx bindings for Scala -include all Rx types. In particular this release focuses on fleshing out the bindings for the `Subject` and `Scheduler` -types, as well as aligning the constructor functions for `Observable` with those in the RxJava. - -Expect to see ongoing additions to make the Scala binding match the equivalent underlying Java API, -as well as minor changes in the existing API as we keep fine-tuning the experience on our way to a V1.0 release. - -Observer --------- - -In this release we have made the `asJavaObserver` property in `Observable[T]`as well the the factory method in the -companion object that takes an `rx.Observer` private to the Scala bindings package, thus properly hiding irrelevant -implementation details from the user-facing API. The `Observer[T]` trait now looks like a clean, native Scala type: - -```scala -trait Observer[-T] { - def onNext(value: T): Unit - def onError(error: Throwable): Unit - def onCompleted(): Unit -} - -object Observer {...} -``` - -To create an instance of a specific `Observer`, say `Observer[SensorEvent]` in user code, you can create a new instance -of the `Observer` trait by implementing any of the methods that you care about: -```scala - val printObserver = new Observer[SensorEvent] { - override def onNext(value: SensorEvent): Unit = {...value.toString...} - } -``` - or you can use one of the overloads of the companion `Observer` object by passing in implementations of the `onNext`, - `onError` or `onCompleted` methods. - -Note that typically you do not need to create an `Observer` since all of the methods that accept an `Observer[T]` -(for instance `subscribe`) usually come with overloads that accept the individual methods -`onNext`, `onError`, and `onCompleted` and will automatically create an `Observer` for you under the covers. - -While *technically* it is a breaking change make the `asJavaObserver` property private, you should probably not have -touched `asJavaObserver` in the first place. If you really feel you need to access the underlying `rx.Observer` -call `toJava`. - -Observable ----------- - -Just like for `Observer`, the `Observable` trait now also hides its `asJavaObservable` property and makes the constructor -function in the companion object that takes an `rx.Observable` private (but leaves the companion object itself public). -Again, while *technically* this is a breaking change, this should not have any influence on user code. - -```scala -trait Observable[+T] { - def subscribe(observer: Observer[T]): Subscription = {...} - def apply(observer: Observer[T]): Subscription = {...} - ... -} -object Observable { - def create[T](func: Observer[T] => Subscription): Observable[T] = {...} - ... -} -``` - -The major changes in `Observable` are wrt to the factory methods where too libral use of overloading of the `apply` -method hindered type inference and made Scala code look unnecessarily different than that in other language bindings. -All factory methods now have their own name corresponding to the Java and .NET operators -(plus overloads that take a `Scheduler`). - -* `def from[T](future: Future[T]): Observable[T]`, -* `def from[T](iterable: Iterable[T]): Observable[T]`, -* `def error[T](exception: Throwable): Observable[T]`, -* `def empty[T]: Observable[T]`, -* `def items[T](items: T*): Observable[T], -* Extension method on `toObservable: Observable[T]` on `List[T]`. - -In the *pre-release* of this version, we expose both `apply` and `create` for the mother of all creation functions. -We would like to solicit feedback which of these two names is preferred -(or both, but there is a high probability that only one will be chosen). - -* `def apply[T](subscribe: Observer[T]=>Subscription): Observable[T]` -* `def create[T](subscribe: Observer[T] => Subscription): Observable[T]` - -Subject -------- - -The `Subject` trait now also hides the underlying Java `asJavaSubject: rx.subjects.Subject[_ >: T, _<: T]` -and takes only a single *invariant* type parameter `T`. all existing implementations of `Subject` are parametrized -by a single type, and this reflects that reality. - -```scala -trait Subject[T] extends Observable[T] with Observer[T] {} -object Subject { - def apply(): Subject[T] = {...} -} -``` -For each kind of subject, there is a class with a private constructor and a companion object that you should use -to create a new kind of subject. The subjects that are available are: - -* `AsyncSubject[T]()`, -* `BehaviorSubject[T](value)`, -* `Subject[T]()`, -* `ReplaySubject[T]()`. - -The latter is still missing various overloads http://msdn.microsoft.com/en-us/library/hh211810(v=vs.103).aspx which -you can expect to appear once they are added to the underlying RxJava implementation. - -Compared with release 0.15.1, the breaking changes in `Subject` for this release are -making `asJavaSubject` private, and collapsing its type parameters, neither of these should cause trouble, -and renaming `PublishSubject` to `Subject`. - -Schedulers ----------- - -The biggest breaking change compared to the 0.15.1 release is giving `Scheduler` the same structure as the other types. -The trait itself remains unchanged, except that we made the underlying Java representation hidden as above. -as part of this reshuffling, the scheduler package has been renamed from `rx.lang.scala.concurrency` -to `rx.lang.scala.schedulers`. There is a high probability that this package renaming will also happen in RxJava. - -```scala -trait Scheduler {...} -``` - -In the previous release, you created schedulers by selecting them from the `Schedulers` object, -as in `Schedulers.immediate` or `Schedulers.newThread` where each would return an instance of the `Scheduler` trait. -However, several of the scheduler implementations have additional methods, such as the `TestScheduler`, -which already deviated from the pattern. - -In this release, we changed this to make scheduler more like `Subject` and provide a family of schedulers -that you create using their factory function: - -* `CurrentThreadScheduler()`, -* `ExecutorScheduler(executor)`, -* `ImmediateScheduler()`, -* `NewThreadScheduler()`, -* `ScheduledExecutorServiceScheduler(scheduledExecutorService)`, -* `TestScheduler()`, -* `ThreadPoolForComputationScheduler()`, -* `ThreadPoolForIOScheduler()`. - -In the future we expect that this list will grow further with new schedulers as they are imported from .NET -(http://msdn.microsoft.com/en-us/library/system.reactive.concurrency(v=vs.103).aspx). - -To make your code compile in the new release you will have to change all occurrences of `Schedulers.xxx` -into `XxxScheduler()`, and import `rx.lang.scala.schedulers` instead of `rx.lang.scala.schedulers`. - -Subscriptions -------------- - -The `Subscription` trait in Scala now has `isUnsubscribed` as a member, effectively collapsing the old `Subscription` -and `BooleanSubscription`, and the latter has been removed from the public surface. Pending a bug fix in RxJava, -`SerialSubscription` implements its own `isUnsubscribed`. - - -```scala -trait Subscription { - def unsubscribe(): Unit = { ... } - def isUnsubscribed: Boolean = ... -} - -object Subscription {...} - ``` - - To create a `Subscription` use one of the following factory methods: - - * `Subscription{...}`, `Subscription()`, - * `CompositeSubscription(subscriptions)`, - * `MultipleAssignmentSubscription()`, - * `SerialSubscription()`. - - In case you do feel tempted to call `new Subscription{...}` directly make sure you wire up `isUnsubscribed` - and `unsubscribe()` properly, but for all practical purposes you should just use one of the factory methods. - -Notifications -------------- - -All underlying wrapped `Java` types in the `Notification` trait are made private like all previous types. The companion -objects of `Notification` now have both constructor (`apply`) and extractor (`unapply`) functions: - -```scala -object Notification {...} -trait Notification[+T] { - override def equals(that: Any): Boolean = {...} - override def hashCode(): Int = {...} - def apply[R](onNext: T=>R, onError: Throwable=>R, onCompleted: ()=>R): R = {...} -} -``` -The nested companion objects of `Notification` now have both constructor (`apply`) and extractor (`unapply`) functions: -```scala -object Notification { - object OnNext { def apply(...){}; def unapply(...){...} } - object OnError { def apply(...){}; def unapply(...){...} } - object OnCompleted { def apply(...){}; def unapply(...){...} } -} -``` -To construct a `Notification`, you import `rx.lang.scala.Notification._` and use `OnNext("hello")`, -or `OnError(new Exception("Oops!"))`, or `OnCompleted()`. - -To pattern match on a notification you create a partial function like so: `case Notification.OnNext(v) => { ... v ... }`, -or you use the `apply` function to pass in functions for each possibility. - -There are no breaking changes for notifications. - -Java Interop Helpers --------------------- - -Since the Scala traits *wrap* the underlying Java types, yoo may occasionally will have to wrap an unwrap -between the two representations. The `JavaConversion` object provides helper functions of the form `toJavaXXX` and -`toScalaXXX` for this purpose, properly hiding how precisely the wrapped types are stored. -Note the (un)wrap conversions are defined as implicits in Scala, but in the unlikely event that you do need them -be kind to the reader of your code and call them explicitly. - -```scala -object JavaConversions { - import language.implicitConversions - - implicit def toJavaNotification[T](s: Notification[T]): rx.Notification[_ <: T] = {...} - implicit def toScalaNotification[T](s: rx.Notification[_ <: T]): Notification[T] = {...} - implicit def toJavaSubscription(s: Subscription): rx.Subscription = {...} - implicit def toScalaSubscription(s: rx.Subscription): Subscription = {...} - implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = {...} - implicit def javaSchedulerToScalaScheduler(s: rx.Scheduler): Scheduler = {...} - implicit def toJavaObserver[T](s: Observer[T]): rx.Observer[_ >: T] = {...} - implicit def toScalaObserver[T](s: rx.Observer[_ >: T]): Observer[T] = {...} - implicit def toJavaObservable[T](s: Observable[T]): rx.Observable[_ <: T] = {...} - implicit def toScalaObservable[T](observable: rx.Observable[_ <: T]): Observable[T] = {...} -} -``` \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/TODO.md b/language-adaptors/rxjava-scala/TODO.md deleted file mode 100644 index f8965cc498..0000000000 --- a/language-adaptors/rxjava-scala/TODO.md +++ /dev/null @@ -1,46 +0,0 @@ - -TODOs for Scala Adapter ------------------------ - -This is a (probably incomplete) list of what still needs to be done in the Scala adaptor. - -TODOs which came up at the meeting with Erik Meijer on 2013-10-11: - -* Rename the factory methods in `object Observable`, considering that the most important is the one taking an `Observer => Subscription` function (the "king" according to Erik). Thunk to Subscription conversion (?), also consider Jason's [comments](https://github.com/Netflix/RxJava/commit/c1596253fc5567b7cc37d20128374d189471ff79). A useful trick might also be to have `apply(T, T, T*)` instead of just `apply(T*)`. -* Factory methods for observables and instance methods should take implicit scheduler, default is different per method (Isn't this a contradiction? In other words: If I call a method without providing a scheduler, should the default scheduler be used or the implicit that I provided?) Find in .NET source the list of which schedulers goes with which operators by default. If no other default, use NewThreadScheduler. Note that there are methods in Scala Observable which should have an overload taking a Scheduler, but do not yet have it! Also remember Erik saying that he would like to "minimize magically injected concurrency". -* Convert executor to scheduler -* Check if TestScheduler added in 0.14.3 is sufficient -* Infinite Iterables: the java Observable.from version unfolds an iterable, even it is infinite. Should be fixed in java. -* subscribe methods: There are many overloads, but still not all combinations one might need. Make this nicer and complete, maybe using default arguments. Also try to make sure that `subscribe(println)` works, not only `subscribe(println(_))`. `foreach(println)` works on collections, but not on `subscribe(println)`, because subscribe is overloaded. -* Currently all Swing examples use Swing directly, without using the Scala wrappers around Swing. Make sure that everything is nice if the Scala wrappers around Swing are used, eg `Reaction { case clicked: ButtonClicked => … }` -- avoid default case, check out the hooks for partial function applyOrElse to avoid double pattern matching -* There are no examples yet using `async`, but `async` will be used in the course. Write examples and check if everything works as expected when combined with `async`. -* Futures: For the moment, just add a Future->Observable converter method to `object Observable`. Later, think if `Future[T] extends Observable[T]`. -* Operator `delay`: Once Erik has commented on [this](https://github.com/Netflix/RxJava/pull/384), make sure this operator is added accordingly to RxJava and then to RxScala - -Some more TODOs: - -* Integrating Scala Futures: Should there be a common base interface for Futures and Observables? And if all subscribers of an Observable wrapping a Future unsubscribe, the Future should be cancelled, but Futures do not support cancellation. -* Add methods present in Scala collections library, but not in RxJava, e.g. aggregate à la Scala, collect, tails, ... -* combineLatest with arities > 2 -* Avoid text duplication in scaladoc using templates, add examples, distinction between use case signature and full signature -* other small TODOs in source code -* always: keep Scala Observable in sync with Java Observable - - -### Appendix: - -`println` example: - - List(1, 2).foreach(println) - Observable(1, 2).toBlockingObservable.foreach(println) - - Observable(1, 2).subscribe(println) // does not work - - class Ob[+T] { - def subscribe(onNext: T => Unit) = ??? - } - val o2 = new Ob[Int] - o2.subscribe(println) // works - - - diff --git a/language-adaptors/rxjava-scala/build.gradle b/language-adaptors/rxjava-scala/build.gradle deleted file mode 100644 index 7c4b9f72f4..0000000000 --- a/language-adaptors/rxjava-scala/build.gradle +++ /dev/null @@ -1,84 +0,0 @@ -apply plugin: 'scala' -apply plugin: 'osgi' - -tasks.withType(ScalaCompile) { - scalaCompileOptions.fork = true - scalaCompileOptions.unchecked = true - scalaCompileOptions.setAdditionalParameters(['-feature']) - - configure(scalaCompileOptions.forkOptions) { - memoryMaximumSize = '1g' - jvmArgs = ['-XX:MaxPermSize=512m'] - } -} - -sourceSets { - main { - scala { - srcDir 'src/main/scala' - } - } - test { - scala { - srcDir 'src/main/scala' - srcDir 'src/test/scala' - srcDir 'src/examples/scala' - //srcDir 'src/examples/java' - } - java.srcDirs = [] - } - examples { - // It seems that in Gradle, the dependency "compileScala depends on compileJava" is hardcoded, - // or at least not meant to be removed. - // However, compileScala also runs javac at the very end, so we just add the Java sources to - // the scala source set: - scala { - srcDir 'src/examples/scala' - //srcDir 'src/examples/java' - } - java.srcDirs = [] - } -} - -dependencies { - compile 'org.scala-lang:scala-library:2.10.+' - - compile project(':rxjava-core') - - testCompile 'junit:junit-dep:4.10' - testCompile 'org.mockito:mockito-core:1.8.5' - testCompile 'org.scalatest:scalatest_2.10:1.9.1' -} - -tasks.compileScala { - classpath = classpath + configurations.compile -} - -tasks.compileExamplesScala { - classpath = classpath + files(compileScala.destinationDir) + (configurations.compile + configurations.testCompile) -} - -// Add RxJava core to Scaladoc input: -// tasks.scaladoc.source(project(':rxjava-core').tasks.getByPath(':rxjava-core:compileJava').source) -// println("-------") -// println(tasks.scaladoc.source.asPath) - -task test(overwrite: true, dependsOn: testClasses) << { - ant.taskdef(name: 'scalatest', - classname: 'org.scalatest.tools.ScalaTestAntTask', - classpath: configurations.testCompile.asPath + ':' + configurations.testRuntime.asPath + ":" + compileScala.destinationDir - ) - ant.scalatest(runpath: sourceSets.test.output.classesDir, - haltonfailure: 'true', - fork: 'false') {reporter(type: 'stdout')} -} - -jar { - manifest { - name = 'rxjava-scala' - instruction 'Bundle-Vendor', 'Netflix' - instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava' - instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,!org.scalatest.*,*' - instruction 'Fragment-Host', 'com.netflix.rxjava.core' - } -} diff --git a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java b/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java deleted file mode 100644 index 5d1acf5361..0000000000 --- a/language-adaptors/rxjava-scala/src/examples/java/rx/lang/scala/examples/MovieLibUsage.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package rx.lang.scala.examples; - -import rx.Observable; -import rx.functions.Action1; -import rx.lang.scala.examples.Movie; -import rx.lang.scala.examples.MovieLib; -import static rx.lang.scala.JavaConversions.toScalaObservable; - -public class MovieLibUsage { - - public static void main(String[] args) { - - Observable<Movie> movies = Observable.from( - new Movie(3000), - new Movie(1000), - new Movie(2000) - ); - - MovieLib lib = new MovieLib(toScalaObservable(movies)); - - lib.longMovies().asJavaObservable().subscribe(new Action1<Movie>() { - - @Override - public void call(Movie m) { - System.out.println("A movie of length " + m.lengthInSeconds() + "s"); - } - }); - } - -} diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala deleted file mode 100644 index 8e2ce5b29b..0000000000 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/MovieLib.scala +++ /dev/null @@ -1,30 +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.examples - -import rx.lang.scala.Observable - -class Movie(val lengthInSeconds: Int) { } - -class MovieLib(val moviesStream: Observable[Movie]) { - - val threshold = 1200 - - def shortMovies: Observable[Movie] = moviesStream.filter(_.lengthInSeconds <= threshold) - - def longMovies: Observable[Movie] = moviesStream.filter(_.lengthInSeconds > threshold) - -} diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala deleted file mode 100644 index af0d71e47d..0000000000 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/Olympics.scala +++ /dev/null @@ -1,103 +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.examples - -import rx.lang.scala.Observable -import scala.concurrent.duration._ -import scala.language.postfixOps - -object Olympics { - case class Medal(val year: Int, val games: String, val discipline: String, val medal: String, val athlete: String, val country: String) - - def mountainBikeMedals: Observable[Medal] = Observable.items( - duration(100 millis), // a short delay because medals are only awarded some time after the Games began - Observable.items( - Medal(1996, "Atlanta 1996", "cross-country men", "Gold", "Bart BRENTJENS", "Netherlands"), - Medal(1996, "Atlanta 1996", "cross-country women", "Gold", "Paola PEZZO", "Italy"), - Medal(1996, "Atlanta 1996", "cross-country men", "Silver", "Thomas FRISCHKNECHT", "Switzerland"), - Medal(1996, "Atlanta 1996", "cross-country women", "Silver", "Alison SYDOR", "Canada"), - Medal(1996, "Atlanta 1996", "cross-country men", "Bronze", "Miguel MARTINEZ", "France"), - Medal(1996, "Atlanta 1996", "cross-country women", "Bronze", "Susan DEMATTEI", "United States of America") - ), - fourYearsEmpty, - Observable.items( - Medal(2000, "Sydney 2000", "cross-country women", "Gold", "Paola PEZZO", "Italy"), - Medal(2000, "Sydney 2000", "cross-country women", "Silver", "Barbara BLATTER", "Switzerland"), - Medal(2000, "Sydney 2000", "cross-country women", "Bronze", "Marga FULLANA", "Spain"), - Medal(2000, "Sydney 2000", "cross-country men", "Gold", "Miguel MARTINEZ", "France"), - Medal(2000, "Sydney 2000", "cross-country men", "Silver", "Filip MEIRHAEGHE", "Belgium"), - Medal(2000, "Sydney 2000", "cross-country men", "Bronze", "Christoph SAUSER", "Switzerland") - ), - fourYearsEmpty, - Observable.items( - Medal(2004, "Athens 2004", "cross-country men", "Gold", "Julien ABSALON", "France"), - Medal(2004, "Athens 2004", "cross-country men", "Silver", "Jose Antonio HERMIDA RAMOS", "Spain"), - Medal(2004, "Athens 2004", "cross-country men", "Bronze", "Bart BRENTJENS", "Netherlands"), - Medal(2004, "Athens 2004", "cross-country women", "Gold", "Gunn-Rita DAHLE", "Norway"), - Medal(2004, "Athens 2004", "cross-country women", "Silver", "Marie-Helene PREMONT", "Canada"), - Medal(2004, "Athens 2004", "cross-country women", "Bronze", "Sabine SPITZ", "Germany") - ), - fourYearsEmpty, - Observable.items( - Medal(2008, "Beijing 2008", "cross-country women", "Gold", "Sabine SPITZ", "Germany"), - Medal(2008, "Beijing 2008", "cross-country women", "Silver", "Maja WLOSZCZOWSKA", "Poland"), - Medal(2008, "Beijing 2008", "cross-country women", "Bronze", "Irina KALENTYEVA", "Russian Federation"), - Medal(2008, "Beijing 2008", "cross-country men", "Gold", "Julien ABSALON", "France"), - Medal(2008, "Beijing 2008", "cross-country men", "Silver", "Jean-Christophe PERAUD", "France"), - Medal(2008, "Beijing 2008", "cross-country men", "Bronze", "Nino SCHURTER", "Switzerland") - ), - fourYearsEmpty, - Observable.items( - Medal(2012, "London 2012", "cross-country men", "Gold", "Jaroslav KULHAVY", "Czech Republic"), - Medal(2012, "London 2012", "cross-country men", "Silver", "Nino SCHURTER", "Switzerland"), - Medal(2012, "London 2012", "cross-country men", "Bronze", "Marco Aurelio FONTANA", "Italy"), - Medal(2012, "London 2012", "cross-country women", "Gold", "Julie BRESSET", "France"), - Medal(2012, "London 2012", "cross-country women", "Silver", "Sabine SPITZ", "Germany"), - Medal(2012, "London 2012", "cross-country women", "Bronze", "Georgia GOULD", "United States of America") - ) - ).concat - - // speed it up :D - val oneYear = 1000.millis - - //val neverUsedDummyMedal = Medal(3333, "?", "?", "?", "?", "?") - - /** runs an infinite loop, and returns Bottom type (Nothing) */ - def getNothing: Nothing = { - println("You shouldn't have called this method ;-)") - getNothing - } - - /** returns an Observable which emits no elements and completes after a duration of d */ - def duration(d: Duration): Observable[Nothing] = Observable.interval(d).take(1).filter(_ => false).map(_ => getNothing) - - def fourYearsEmpty: Observable[Medal] = duration(4*oneYear) - - def yearTicks: Observable[Int] = - (Observable.from(1996 to 2014) zip (Observable.items(-1) ++ Observable.interval(oneYear))).map(_._1) - - /* - def fourYearsEmptyOld: Observable[Medal] = { - // TODO this should return an observable which emits nothing during fourYears and then completes - // Because of https://github.com/Netflix/RxJava/issues/388, we get non-terminating tests - // And this https://github.com/Netflix/RxJava/pull/289#issuecomment-24738668 also causes problems - // So we don't use this: - Observable.interval(fourYears).take(1).map(i => neverUsedDummyMedal).filter(m => false) - // But we just return empty, which completes immediately - // Observable.empty - }*/ - -} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala deleted file mode 100644 index 2d1e5b9ab4..0000000000 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala +++ /dev/null @@ -1,1443 +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.examples - -import java.io.IOException -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit - -import scala.concurrent.Await -import scala.collection.mutable -import scala.concurrent.duration.Duration -import scala.concurrent.duration.DurationInt -import scala.concurrent.duration.DurationLong -import scala.language.postfixOps -import scala.language.implicitConversions - -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Assert.assertFalse -import org.junit.Ignore -import org.junit.Test -import org.scalatest.junit.JUnitSuite - -import rx.lang.scala._ -import rx.lang.scala.schedulers._ - -/** - * Demo how the different operators can be used. In Eclipse, you can right-click - * a test and choose "Run As" > "Scala JUnit Test". - * - * For each operator added to Observable.java, we add a little usage demo here. - * It does not need to test the functionality (that's already done by the tests in - * RxJava core), but it should demonstrate how it can be used, to make sure that - * the method signature makes sense. - */ -@Ignore // Since this doesn't do automatic testing, don't increase build time unnecessarily -class RxScalaDemo extends JUnitSuite { - - @Test def subscribeExample() { - val o = Observable.items(1, 2, 3) - - // Generally, we have two methods, `subscribe` and `foreach`, to listen to the messages from an Observable. - // `foreach` is just an alias to `subscribe`. - o.subscribe( - n => println(n), - e => e.printStackTrace(), - () => println("done") - ) - - o.foreach( - n => println(n), - e => e.printStackTrace(), - () => println("done") - ) - - // For-comprehension is also an alternative, if you are only interested in `onNext` - for (i <- o) { - println(i) - } - } - - @Test def intervalExample() { - val o = Observable.interval(200 millis).take(5) - o.subscribe(n => println("n = " + n)) - - // need to wait here because otherwise JUnit kills the thread created by interval() - waitFor(o) - - println("done") - } - - def msTicks(start: Long, step: Long): Observable[Long] = { - // will be easier once we have Observable.generate method - Observable.interval(step millis) map (_ * step + start) - } - - def prefixedTicks(start: Long, step: Long, prefix: String): Observable[String] = { - msTicks(start, step).map(prefix + _) - } - - @Test def testTicks() { - val o = prefixedTicks(5000, 500, "t = ").take(5) - o.subscribe(output(_)) - waitFor(o) - } - - @Test def testSwitch() { - // We do not have ultimate precision: Sometimes, 747 gets through, sometimes not - val o = Observable.interval(1000 millis).map(n => prefixedTicks(0, 249, s"Observable#$n: ")) - .switch.take(16) - o.subscribe(output(_)) - waitFor(o) - } - - @Test def testSwitchOnObservableOfInt() { - // Correctly rejected with error - // "Cannot prove that Observable[Int] <:< Observable[Observable[U]]" - // val o = Observable(1, 2).switch - } - - @Test def testObservableComparison() { - val first = Observable.from(List(10, 11, 12)) - val second = Observable.from(List(10, 11, 12)) - - val b = (first zip second) forall { case (a, b) => a == b } - - assertTrue(b.toBlocking.single) - } - - @Test def testObservableComparisonWithForComprehension() { - val first = Observable.from(List(10, 11, 12)) - val second = Observable.from(List(10, 11, 12)) - - val booleans = for ((n1, n2) <- (first zip second)) yield (n1 == n2) - - val b1 = booleans.forall(identity) - - assertTrue(b1.toBlocking.single) - } - - @Test def testStartWithIsUnnecessary() { - val before = List(-2, -1, 0).toObservable - val source = List(1, 2, 3).toObservable - println((before ++ source).toBlocking.toList) - } - - @Test def mergeTwoExample() { - val slowNumbers = Observable.interval(400 millis).take(5).map("slow " + _) - val fastNumbers = Observable.interval(200 millis).take(10).map("fast " + _) - val o = (slowNumbers merge fastNumbers) - o.subscribe(output(_)) - waitFor(o) - } - - def myInterval(period: Long): Observable[String] = { - Observable.interval(period.millis).map(n => s"Obs-$period emits $n") - } - - @Test def flattenManyExample() { - val o = Observable.interval(500 millis).map(n => myInterval((n+1)*100)) - val stopper = Observable.interval(5 seconds) - o.flatten.takeUntil(stopper).toBlocking.foreach(println(_)) - } - - @Test def flattenSomeExample() { - // To merge some observables which are all known already: - List( - Observable.interval(200 millis), - Observable.interval(400 millis), - Observable.interval(800 millis) - ).toObservable.flatten.take(12).toBlocking.foreach(println(_)) - } - - @Test def flattenExample() { - List( - Observable.interval(200 millis).map(_ => 1).take(5), - Observable.interval(200 millis).map(_ => 2).take(5), - Observable.interval(200 millis).map(_ => 3).take(5), - Observable.interval(200 millis).map(_ => 4).take(5) - ).toObservable.flatten.toBlocking.foreach(println(_)) - } - - @Test def flattenExample2() { - List( - Observable.interval(200 millis).map(_ => 1).take(5), - Observable.interval(200 millis).map(_ => 2).take(5), - Observable.interval(200 millis).map(_ => 3).take(5), - Observable.interval(200 millis).map(_ => 4).take(5) - ).toObservable.flatten(2).toBlocking.foreach(println(_)) - } - - @Test def tumblingBufferExample() { - val o = Observable.from(1 to 18) - o.tumblingBuffer(5).subscribe((l: Seq[Int]) => println(l.mkString("[", ", ", "]"))) - } - - @Test def tumblingBufferExample2() { - val o = Observable.from(1 to 18).zip(Observable.interval(100 millis)).map(_._1) - val boundary = Observable.interval(500 millis) - o.tumblingBuffer(boundary).toBlocking.foreach((l: Seq[Int]) => println(l.mkString("[", ", ", "]"))) - } - - @Test def slidingBufferExample() { - val o = Observable.from(1 to 18).slidingBuffer(4, 2) - o.subscribe(println(_)) - } - - @Test def slidingBufferExample2() { - val open = Observable.interval(300 millis) - val closing = Observable.interval(600 millis) - val o = Observable.interval(100 millis).take(20).slidingBuffer(open)(_ => closing) - o.zipWithIndex.toBlocking.foreach { - case (seq, i) => println(s"Observable#$i emits $seq") - } - } - - @Test def slidingBufferExample3() { - val o = Observable.from(1 to 18).zip(Observable.interval(100 millis)).map(_._1) - o.slidingBuffer(500 millis, 200 millis).toBlocking.foreach((l: Seq[Int]) => println(l.mkString("[", ", ", "]"))) - } - - @Test def tumblingExample() { - (for ((o, i) <- Observable.from(1 to 18).tumbling(5).zipWithIndex; n <- o) - yield s"Observable#$i emits $n" - ).subscribe(output(_)) - } - - @Test def tumblingExample2() { - val windowObservable = Observable.interval(500 millis) - val o = Observable.from(1 to 20).zip(Observable.interval(100 millis)).map(_._1) - (for ((o, i) <- o.tumbling(windowObservable).zipWithIndex; n <- o) - yield s"Observable#$i emits $n" - ).toBlocking.foreach(println) - } - - @Test def slidingExample() { - val o = Observable.from(1 to 18).sliding(4, 2) - (for ((o, i) <- o.zipWithIndex; - n <- o) - yield s"Observable#$i emits $n" - ).toBlocking.foreach(println) - } - - @Test def slidingExample2() { - val o = Observable.interval(100 millis).take(20).sliding(500 millis, 200 millis) - (for ((o, i) <- o.zipWithIndex; - n <- o) - yield s"Observable#$i emits $n" - ).toBlocking.foreach(println) - } - - @Test def slidingExample3() { - val open = Observable.interval(300 millis) - val closing = Observable.interval(600 millis) - val o = Observable.interval(100 millis).take(20).sliding(open)(_ => closing) - (for ((o, i) <- o.zipWithIndex; - n <- o) - yield s"Observable#$i emits $n" - ).toBlocking.foreach(println) - } - - @Test def testReduce() { - assertEquals(10, List(1, 2, 3, 4).toObservable.reduce(_ + _).toBlocking.single) - } - - @Test def testForeach() { - val numbers = Observable.interval(200 millis).take(3) - - // foreach is not available on normal Observables: - // for (n <- numbers) println(n+10) - - // but on BlockingObservable, it is: - for (n <- numbers.toBlocking) println(n+10) - } - - @Test def testForComprehension() { - val observables = List(List(1, 2, 3).toObservable, List(10, 20, 30).toObservable).toObservable - val squares = (for (o <- observables; i <- o if i % 2 == 0) yield i*i) - assertEquals(squares.toBlocking.toList, List(4, 100, 400, 900)) - } - - @Test def nextExample() { - val o = Observable.interval(100 millis).take(20) - for(i <- o.toBlocking.next) { - println(i) - Thread.sleep(200) - } - } - - @Test def latestExample() { - val o = Observable.interval(100 millis).take(20) - for(i <- o.toBlocking.latest) { - println(i) - Thread.sleep(200) - } - } - - @Test def toFutureExample() { - val o = Observable.interval(500 millis).take(1) - val r = Await.result(o.toBlocking.toFuture, 2 seconds) - println(r) - } - - @Test def testTwoSubscriptionsToOneInterval() { - val o = Observable.interval(100 millis).take(8) - o.subscribe( - i => println(s"${i}a (on thread #${Thread.currentThread().getId})") - ) - o.subscribe( - i => println(s"${i}b (on thread #${Thread.currentThread().getId})") - ) - waitFor(o) - } - - @Test def schedulersExample() { - val o = Observable.interval(100 millis).take(8) - o.observeOn(NewThreadScheduler()).subscribe( - i => println(s"${i}a (on thread #${Thread.currentThread().getId})") - ) - o.observeOn(NewThreadScheduler()).subscribe( - i => println(s"${i}b (on thread #${Thread.currentThread().getId})") - ) - waitFor(o) - } - - @Test def testGroupByThenFlatMap() { - val m = List(1, 2, 3, 4).toObservable - val g = m.groupBy(i => i % 2) - val t = g.flatMap((p: (Int, Observable[Int])) => p._2) - assertEquals(List(1, 2, 3, 4), t.toBlocking.toList) - } - - @Test def testGroupByThenFlatMapByForComprehension() { - val m = List(1, 2, 3, 4).toObservable - val g = m.groupBy(i => i % 2) - val t = for ((i, o) <- g; n <- o) yield n - assertEquals(List(1, 2, 3, 4), t.toBlocking.toList) - } - - @Test def testGroupByThenFlatMapByForComprehensionWithTiming() { - val m = Observable.interval(100 millis).take(4) - val g = m.groupBy(i => i % 2) - val t = for ((i, o) <- g; n <- o) yield n - assertEquals(List(0, 1, 2, 3), t.toBlocking.toList) - } - - @Test def timingTest() { - val firstOnly = false - val numbersByModulo3 = Observable.interval(1000 millis).take(9).groupBy(_ % 3) - - (for ((modulo, numbers) <- numbersByModulo3) yield { - println("Observable for modulo" + modulo + " started") - - if (firstOnly) numbers.take(1) else numbers - }).flatten.toBlocking.foreach(println(_)) - } - - @Test def timingTest1() { - val numbersByModulo3 = Observable.interval(1000 millis).take(9).groupBy(_ % 3) - - val t0 = System.currentTimeMillis - - (for ((modulo, numbers) <- numbersByModulo3) yield { - println("Observable for modulo" + modulo + " started at t = " + (System.currentTimeMillis - t0)) - numbers.map(n => s"${n} is in the modulo-$modulo group") - }).flatten.toBlocking.foreach(println(_)) - } - - @Test def testOlympicYearTicks() { - Olympics.yearTicks.subscribe(println(_)) - waitFor(Olympics.yearTicks) - } - - @Test def groupByExample() { - val medalsByCountry = Olympics.mountainBikeMedals.groupBy(medal => medal.country) - - val firstMedalOfEachCountry = - for ((country, medals) <- medalsByCountry; firstMedal <- medals.take(1)) yield firstMedal - - firstMedalOfEachCountry.subscribe(medal => { - println(s"${medal.country} wins its first medal in ${medal.year}") - }) - - Olympics.yearTicks.subscribe(year => println(s"\nYear $year starts.")) - - waitFor(Olympics.yearTicks) - } - - @Test def groupByUntilExample() { - val numbers = Observable.interval(250 millis).take(14) - val grouped = numbers.groupByUntil(x => x % 2){ case (key, obs) => obs.filter(x => x == 7) } - val sequenced = (grouped.map({ case (key, obs) => obs.toSeq })).flatten - sequenced.subscribe(x => println(s"Emitted group: $x")) - } - - @Test def groupByUntilExample2() { - val numbers = Observable.interval(250 millis).take(14) - val grouped = numbers.groupByUntil(x => x % 2, x => x * 10){ case (key, obs) => Observable.interval(2 seconds) } - val sequenced = (grouped.map({ case (key, obs) => obs.toSeq })).flatten - sequenced.toBlocking.foreach(x => println(s"Emitted group: $x")) - } - - @Test def combineLatestExample() { - val firstCounter = Observable.interval(250 millis) - val secondCounter = Observable.interval(550 millis) - val combinedCounter = firstCounter.combineLatestWith(secondCounter)(List(_, _)) take 10 - - combinedCounter subscribe {x => println(s"Emitted group: $x")} - waitFor(combinedCounter) - } - - @Test def combineLatestExample2() { - val firstCounter = Observable.interval(250 millis) - val secondCounter = Observable.interval(550 millis) - val thirdCounter = Observable.interval(850 millis) - val sources = Seq(firstCounter, secondCounter, thirdCounter) - val combinedCounter = Observable.combineLatest(sources)(_.toList).take(10) - - combinedCounter subscribe {x => println(s"Emitted group: $x")} - waitFor(combinedCounter) - } - - @Test def olympicsExampleWithoutPublish() { - val medals = Olympics.mountainBikeMedals.doOnEach(_ => println("onNext")) - medals.subscribe(println(_)) // triggers an execution of medals Observable - waitFor(medals) // triggers another execution of medals Observable - } - - @Test def olympicsExampleWithPublish() { - val medals = Olympics.mountainBikeMedals.doOnEach(_ => println("onNext")).publish - medals.subscribe(println(_)) // triggers an execution of medals Observable - medals.connect - waitFor(medals) // triggers another execution of medals Observable - } - - @Test def exampleWithoutPublish() { - val unshared = Observable.from(1 to 4) - unshared.subscribe(n => println(s"subscriber 1 gets $n")) - unshared.subscribe(n => println(s"subscriber 2 gets $n")) - } - - @Test def exampleWithPublish() { - val unshared = Observable.from(1 to 4) - val shared = unshared.publish - shared.subscribe(n => println(s"subscriber 1 gets $n")) - shared.subscribe(n => println(s"subscriber 2 gets $n")) - shared.connect - } - - @Test def exampleWithPublish2() { - val unshared = Observable.from(1 to 4) - val shared = unshared.publish(0) - shared.subscribe(n => println(s"subscriber 1 gets $n")) - shared.subscribe(n => println(s"subscriber 2 gets $n")) - shared.connect - } - - @Test def exampleWithPublish3() { - val o = Observable.interval(100 millis).take(5).publish((o: Observable[Long]) => o.map(_ * 2)) - o.subscribe(n => println(s"subscriber 1 gets $n")) - o.subscribe(n => println(s"subscriber 2 gets $n")) - Thread.sleep(1000) - } - - @Test def exampleWithPublish4() { - val o = Observable.interval(100 millis).take(5).publish((o: Observable[Long]) => o.map(_ * 2), -1L) - o.subscribe(n => println(s"subscriber 1 gets $n")) - o.subscribe(n => println(s"subscriber 2 gets $n")) - Thread.sleep(1000) - } - - def doLater(waitTime: Duration, action: () => Unit): Unit = { - Observable.interval(waitTime).take(1).subscribe(_ => action()) - } - - @Test def exampleWithoutReplay() { - val numbers = Observable.interval(1000 millis).take(6) - val sharedNumbers = numbers.publish - sharedNumbers.subscribe(n => println(s"subscriber 1 gets $n")) - sharedNumbers.connect - // subscriber 2 misses 0, 1, 2! - doLater(3500 millis, () => { sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) }) - waitFor(sharedNumbers) - } - - @Test def exampleWithReplay() { - val numbers = Observable.interval(1000 millis).take(6) - val sharedNumbers = numbers.replay - sharedNumbers.subscribe(n => println(s"subscriber 1 gets $n")) - sharedNumbers.connect - // subscriber 2 subscribes later but still gets all numbers - doLater(3500 millis, () => { sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) }) - waitFor(sharedNumbers) - } - - @Test def exampleWithReplay2() { - val numbers = Observable.interval(100 millis).take(10) - val sharedNumbers = numbers.replay(3) - sharedNumbers.subscribe(n => println(s"subscriber 1 gets $n")) - sharedNumbers.connect - // subscriber 2 subscribes later but only gets the 3 buffered numbers and the following numbers - Thread.sleep(700) - sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) - waitFor(sharedNumbers) - } - - @Test def exampleWithReplay3() { - val numbers = Observable.interval(100 millis).take(10) - val sharedNumbers = numbers.replay(300 millis) - sharedNumbers.subscribe(n => println(s"subscriber 1 gets $n")) - sharedNumbers.connect - // subscriber 2 subscribes later but only gets the buffered numbers and the following numbers - Thread.sleep(700) - sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) - waitFor(sharedNumbers) - } - - @Test def exampleWithReplay4() { - val numbers = Observable.interval(100 millis).take(10) - val sharedNumbers = numbers.replay(2, 300 millis) - sharedNumbers.subscribe(n => println(s"subscriber 1 gets $n")) - sharedNumbers.connect - // subscriber 2 subscribes later but only gets the buffered numbers and the following numbers - Thread.sleep(700) - sharedNumbers.subscribe(n => println(s"subscriber 2 gets $n")) - waitFor(sharedNumbers) - } - - @Test def exampleWithReplay5() { - val numbers = Observable.interval(100 millis).take(10) - val sharedNumbers = numbers.replay[Long, Long]((o: Observable[Long]) => o.map(_ * 2)) - sharedNumbers.subscribe(n => println(s"subscriber gets $n")) - waitFor(sharedNumbers) - } - - @Test def testSingleOption() { - assertEquals(None, List(1, 2).toObservable.toBlocking.singleOption) - assertEquals(Some(1), List(1).toObservable.toBlocking.singleOption) - assertEquals(None, List().toObservable.toBlocking.singleOption) - } - - // We can't put a general average method into Observable.scala, because Scala's Numeric - // does not have scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum) - def doubleAverage(o: Observable[Double]): Observable[Double] = { - for ((finalSum, finalCount) <- o.foldLeft((0.0, 0))({case ((sum, count), elem) => (sum+elem, count+1)})) - yield finalSum / finalCount - } - - @Test def averageExample() { - println(doubleAverage(Observable.empty).toBlocking.single) - println(doubleAverage(List(0.0).toObservable).toBlocking.single) - println(doubleAverage(List(4.44).toObservable).toBlocking.single) - println(doubleAverage(List(1, 2, 3.5).toObservable).toBlocking.single) - } - - @Test def testSum() { - assertEquals(10, List(1, 2, 3, 4).toObservable.sum.toBlocking.single) - assertEquals(6, List(4, 2).toObservable.sum.toBlocking.single) - assertEquals(0, List[Int]().toObservable.sum.toBlocking.single) - } - - @Test def testProduct() { - assertEquals(24, List(1, 2, 3, 4).toObservable.product.toBlocking.single) - assertEquals(8, List(4, 2).toObservable.product.toBlocking.single) - assertEquals(1, List[Int]().toObservable.product.toBlocking.single) - } - - @Test def mapWithIndexExample() { - // We don't need mapWithIndex because we already have zipWithIndex, which we can easily - // combine with map: - List("a", "b", "c").toObservable.zipWithIndex.map(pair => pair._1 + " has index " + pair._2) - .toBlocking.foreach(println(_)) - - // Or even nicer with for-comprehension syntax: - (for ((letter, index) <- List("a", "b", "c").toObservable.zipWithIndex) yield letter + " has index " + index) - .toBlocking.foreach(println(_)) - } - - // source Observables are all known: - @Test def zip3Example() { - val o = Observable.zip(List(1, 2).toObservable, List(10, 20).toObservable, List(100, 200).toObservable) - (for ((n1, n2, n3) <- o) yield s"$n1, $n2 and $n3") - .toBlocking.foreach(println(_)) - } - - // source Observables are in an Observable: - @Test def zipManyObservableExample() { - val observables = List(List(1, 2).toObservable, List(10, 20).toObservable, List(100, 200).toObservable).toObservable - (for (seq <- Observable.zip(observables)) yield seq.mkString("(", ", ", ")")) - .toBlocking.foreach(println(_)) - } - - /** - * This is a bad way of using `zip` with an `Iterable`: even if the consumer unsubscribes, - * some elements may still be pulled from `Iterable`. - */ - @Test def zipWithIterableBadExample() { - val o1 = Observable.interval(100 millis, IOScheduler()).map(_ * 100).take(3) - val o2 = Observable.from(0 until Int.MaxValue).doOnEach(i => println(i + " from o2")) - o1.zip(o2).toBlocking.foreach(println(_)) - } - - /** - * This is a good way of using `zip` with an `Iterable`: if the consumer unsubscribes, - * no more elements will be pulled from `Iterable`. - */ - @Test def zipWithIterableGoodExample() { - val o1 = Observable.interval(100 millis, IOScheduler()).map(_ * 100).take(3) - val iter = (0 until Int.MaxValue).view.map { - i => { - println(i + " from iter") - i - } - } - o1.zip(iter).toBlocking.foreach(println(_)) - } - - @Test def zipWithExample() { - val xs = Observable.items(1, 3, 5, 7) - val ys = Observable.items(2, 4, 6, 8) - xs.zipWith(ys)(_ * _).subscribe(println(_)) - } - - @Test def takeFirstWithCondition() { - val condition: Int => Boolean = _ >= 3 - assertEquals(3, List(1, 2, 3, 4).toObservable.filter(condition).first.toBlocking.single) - } - - @Test def firstOrDefaultWithCondition() { - val condition: Int => Boolean = _ >= 3 - assertEquals(3, List(1, 2, 3, 4).toObservable.filter(condition).firstOrElse(10).toBlocking.single) - assertEquals(10, List(-1, 0, 1).toObservable.filter(condition).firstOrElse(10).toBlocking.single) - } - - @Test def firstLastSingleExample() { - assertEquals(1, List(1, 2, 3, 4).toObservable.head.toBlocking.single) - assertEquals(1, List(1, 2, 3, 4).toObservable.first.toBlocking.single) - assertEquals(4, List(1, 2, 3, 4).toObservable.last.toBlocking.single) - assertEquals(1, List(1).toObservable.single.toBlocking.single) - - assertEquals(1, List(1, 2, 3, 4).toObservable.toBlocking.head) - assertEquals(1, List(1, 2, 3, 4).toObservable.toBlocking.first) - assertEquals(4, List(1, 2, 3, 4).toObservable.toBlocking.last) - assertEquals(1, List(1).toObservable.toBlocking.single) - } - - @Test def dropExample() { - val o = List(1, 2, 3, 4).toObservable - assertEquals(List(3, 4), o.drop(2).toBlocking.toList) - } - - @Test def dropWithTimeExample() { - val o = List(1, 2, 3, 4).toObservable.zip( - Observable.interval(500 millis, IOScheduler())).map(_._1) // emit every 500 millis - println( - o.drop(1250 millis, IOScheduler()).toBlocking.toList // output List(3, 4) - ) - } - - @Test def dropRightExample() { - val o = List(1, 2, 3, 4).toObservable - assertEquals(List(1, 2), o.dropRight(2).toBlocking.toList) - } - - @Test def dropRightWithTimeExample() { - val o = List(1, 2, 3, 4).toObservable.zip( - Observable.interval(500 millis, IOScheduler())).map(_._1) // emit every 500 millis - println( - o.dropRight(750 millis, IOScheduler()).toBlocking.toList // output List(1, 2) - ) - } - - @Test def dropUntilExample() { - val o = List("Alice", "Bob", "Carlos").toObservable.zip( - Observable.interval(700 millis, IOScheduler())).map(_._1) // emit every 700 millis - val other = List(1).toObservable.delay(1 seconds) - println( - o.dropUntil(other).toBlocking.toList // output List("Bob", "Carlos") - ) - } - - def square(x: Int): Int = { - println(s"$x*$x is being calculated on thread ${Thread.currentThread().getId}") - Thread.sleep(100) // calculating a square is heavy work :) - x*x - } - - def work(o1: Observable[Int]): Observable[String] = { - println(s"map() is being called on thread ${Thread.currentThread().getId}") - o1.map(i => s"The square of $i is ${square(i)}") - } - - @Test def parallelExample() { - val t0 = System.currentTimeMillis() - Observable.from(1 to 10).parallel(work(_)).toBlocking.foreach(println(_)) - println(s"Work took ${System.currentTimeMillis()-t0} ms") - } - - @Test def exampleWithoutParallel() { - val t0 = System.currentTimeMillis() - work(Observable.from(1 to 10)).toBlocking.foreach(println(_)) - println(s"Work took ${System.currentTimeMillis()-t0} ms") - } - - @Test def toSortedList() { - assertEquals(Seq(7, 8, 9, 10), List(10, 7, 8, 9).toObservable.toSeq.map(_.sorted).toBlocking.single) - val f = (a: Int, b: Int) => b < a - assertEquals(Seq(10, 9, 8, 7), List(10, 7, 8, 9).toObservable.toSeq.map(_.sortWith(f)).toBlocking.single) - } - - @Test def timestampExample() { - val timestamped = Observable.interval(100 millis).take(6).timestamp.toBlocking - for ((millis, value) <- timestamped if value > 0) { - println(value + " at t = " + millis) - } - } - - @Test def materializeExample1() { - def printObservable[T](o: Observable[T]): Unit = { - import Notification._ - o.materialize.subscribe(n => n match { - case OnNext(v) => println("Got value " + v) - case OnCompleted => println("Completed") - case OnError(err) => println("Error: " + err.getMessage) - }) - } - - val o1 = Observable.interval(100 millis).take(3) - val o2 = Observable.error(new IOException("Oops")) - printObservable(o1) - printObservable(o2) - Thread.sleep(500) - } - - @Test def materializeExample2() { - import Notification._ - List(1, 2, 3).toObservable.materialize.subscribe(n => n match { - case OnNext(v) => println("Got value " + v) - case OnCompleted => println("Completed") - case OnError(err) => println("Error: " + err.getMessage) - }) - } - - @Test def notificationSubtyping() { - import Notification._ - val oc1: Notification[Nothing] = OnCompleted - val oc2: Notification[Int] = OnCompleted - val oc3: rx.Notification[_ <: Int] = oc2.asJavaNotification - val oc4: rx.Notification[_ <: Any] = oc2.asJavaNotification - } - - @Test def takeWhileWithIndexAlternative { - val condition = true - List("a", "b").toObservable.zipWithIndex.takeWhile{case (elem, index) => condition}.map(_._1) - } - - def calculateElement(index: Int): String = { - println("omg I'm calculating so hard") - index match { - case 0 => "a" - case 1 => "b" - case _ => throw new IllegalArgumentException - } - } - - /** - * This is a bad way of using Observable.create, because even if the consumer unsubscribes, - * all elements are calculated. - */ - @Test def createExampleBad() { - val o = Observable.create[String](observer => { - observer.onNext(calculateElement(0)) - observer.onNext(calculateElement(1)) - observer.onCompleted() - Subscription {} - }) - o.take(1).subscribe(println(_)) - } - - /** - * This is the good way of doing it: If the consumer unsubscribes, no more elements are - * calculated. - */ - @Test def createExampleGood() { - val o = Observable[String](subscriber => { - var i = 0 - while (i < 2 && !subscriber.isUnsubscribed) { - subscriber.onNext(calculateElement(i)) - i += 1 - } - if (!subscriber.isUnsubscribed) subscriber.onCompleted() - }) - o.take(1).subscribe(println(_)) - } - - @Test def createExampleGood2() { - import scala.io.{Codec, Source} - - val rxscala = Observable[String](subscriber => { - try { - val input = new java.net.URL("http://rxscala.github.io/").openStream() - subscriber.add(Subscription { - input.close() - }) - Source.fromInputStream(input)(Codec.UTF8).getLines() - .takeWhile(_ => !subscriber.isUnsubscribed) - .foreach(subscriber.onNext(_)) - if (!subscriber.isUnsubscribed) { - subscriber.onCompleted() - } - } - catch { - case e: Throwable => if (!subscriber.isUnsubscribed) subscriber.onError(e) - } - }).subscribeOn(IOScheduler()) - - val count = rxscala.flatMap(_.split("\\W+").toSeq.toObservable) - .map(_.toLowerCase) - .filter(_ == "rxscala") - .size - println(s"RxScala appears ${count.toBlocking.single} times in http://rxscala.github.io/") - } - - def output(s: String): Unit = println(s) - - /** Subscribes to obs and waits until obs has completed. Note that if you subscribe to - * obs yourself and also call waitFor(obs), all side-effects of subscribing to obs - * will happen twice. - */ - def waitFor[T](obs: Observable[T]): Unit = { - obs.toBlocking.toIterable.last - } - - @Test def doOnCompletedExample(): Unit = { - val o = List("red", "green", "blue").toObservable.doOnCompleted { println("onCompleted") } - o.subscribe(v => println(v), e => e.printStackTrace) - // red - // green - // blue - // onCompleted - } - - @Test def doOnTerminateExample(): Unit = { - val o = List("red", "green", "blue").toObservable.doOnTerminate { println("terminate") } - o.subscribe(v => println(v), e => e.printStackTrace, () => println("onCompleted")) - // red - // green - // blue - // terminate - // onCompleted - } - - @Test def finallyDoExample(): Unit = { - val o = List("red", "green", "blue").toObservable.finallyDo { println("finally") } - o.subscribe(v => println(v), e => e.printStackTrace, () => println("onCompleted")) - // red - // green - // blue - // onCompleted - // finally - } - - @Test def timeoutExample(): Unit = { - val other = List(100L, 200L, 300L).toObservable - val result = Observable.interval(100 millis).timeout(50 millis, other).toBlocking.toList - println(result) - } - - @Test def timeoutExample2(): Unit = { - val firstTimeoutSelector = () => { - Observable.timer(10 seconds, 10 seconds, ComputationScheduler()).take(1) - } - val timeoutSelector = (t: Long) => { - Observable.timer( - (500 - t * 100) max 1 millis, - (500 - t * 100) max 1 millis, - ComputationScheduler()).take(1) - } - val other = List(100L, 200L, 300L).toObservable - val result = Observable.interval(100 millis).timeout(firstTimeoutSelector, timeoutSelector, other).toBlocking.toList - println(result) - } - - @Test def ambExample(): Unit = { - val o1 = List(100L, 200L, 300L).toObservable.delay(4 seconds) - val o2 = List(1000L, 2000L, 3000L).toObservable.delay(2 seconds) - val result = o1.amb(o2).toBlocking.toList - println(result) - } - - @Test def ambWithVarargsExample(): Unit = { - val o1 = List(100L, 200L, 300L).toObservable.delay(4 seconds) - val o2 = List(1000L, 2000L, 3000L).toObservable.delay(2 seconds) - val o3 = List(10000L, 20000L, 30000L).toObservable.delay(4 seconds) - val result = Observable.amb(o1, o2, o3).toBlocking.toList - println(result) - } - - @Test def ambWithSeqExample(): Unit = { - val o1 = List(100L, 200L, 300L).toObservable.delay(4 seconds) - val o2 = List(1000L, 2000L, 3000L).toObservable.delay(2 seconds) - val o3 = List(10000L, 20000L, 30000L).toObservable.delay(4 seconds) - val o = Seq(o1, o2, o3) - val result = Observable.amb(o: _*).toBlocking.toList - println(result) - } - - @Test def delayExample(): Unit = { - val o = List(100L, 200L, 300L).toObservable.delay(2 seconds) - val result = o.toBlocking.toList - println(result) - } - - @Test def delayExample2(): Unit = { - val o = List(100L, 200L, 300L).toObservable.delay(2 seconds, IOScheduler()) - val result = o.toBlocking.toList - println(result) - } - - @Test def delayExample3(): Unit = { - val o = List(100, 500, 200).toObservable.delay( - (i: Int) => Observable.items(i).delay(i millis) - ) - o.toBlocking.foreach(println(_)) - } - - @Test def delayExample4(): Unit = { - val o = List(100, 500, 200).toObservable.delay( - () => Observable.interval(500 millis).take(1), - (i: Int) => Observable.items(i).delay(i millis) - ) - o.toBlocking.foreach(println(_)) - } - - @Test def delaySubscriptionExample(): Unit = { - val o = List(100L, 200L, 300L).toObservable.delaySubscription(2 seconds) - val result = o.toBlocking.toList - println(result) - } - - @Test def delaySubscriptionExample2(): Unit = { - val o = List(100L, 200L, 300L).toObservable.delaySubscription(2 seconds, IOScheduler()) - val result = o.toBlocking.toList - println(result) - } - - @Test def elementAtExample(): Unit = { - val o = List("red", "green", "blue").toObservable - println(o.elementAt(2).toBlocking.single) - } - - @Test def elementAtOrDefaultExample(): Unit = { - val o : Observable[Seq[Char]] = List("red".toList, "green".toList, "blue".toList).toObservable.elementAtOrDefault(3, "black".toSeq) - println(o.toBlocking.single) - } - - @Test def toMapExample1(): Unit = { - val o : Observable[String] = List("alice", "bob", "carol").toObservable - val keySelector = (s: String) => s.head - val m = o.toMap(keySelector) - println(m.toBlocking.single) - } - - @Test def toMapExample2(): Unit = { - val o : Observable[String] = List("alice", "bob", "carol").toObservable - val keySelector = (s: String) => s.head - val valueSelector = (s: String) => s.tail - val m = o.toMap(keySelector, valueSelector) - println(m.toBlocking.single) - } - - @Test def toMapExample3(): Unit = { - val o : Observable[String] = List("alice", "bob", "carol").toObservable - val keySelector = (s: String) => s.head - val valueSelector = (s: String) => s.tail - val mapFactory = () => Map(('s',"tart")) - val m = o.toMap(keySelector, valueSelector, mapFactory) - println(m.toBlocking.single) - } - - @Test def toMultimapExample1(): Unit = { - val o : Observable[String] = List("alice", "bob", "carol", "allen", "clarke").toObservable - val keySelector = (s: String) => s.head - val m = o.toMultimap(keySelector) - println(m.toBlocking.single) - } - - @Test def toMultimapExample2(): Unit = { - val o : Observable[String] = List("alice", "bob", "carol", "allen", "clarke").toObservable - val keySelector = (s: String) => s.head - val valueSelector = (s: String) => s.tail - val m = o.toMultimap(keySelector, valueSelector) - println(m.toBlocking.single) - } - - @Test def toMultimapExample3(): Unit = { - val o: Observable[String] = List("alice", "bob", "carol", "allen", "clarke").toObservable - val keySelector = (s: String) => s.head - val valueSelector = (s: String) => s.tail - val mapFactory = () => mutable.Map('d' -> mutable.Buffer("oug")) - val m = o.toMultimap(keySelector, valueSelector, mapFactory) - println(m.toBlocking.single.mapValues(_.toList)) - } - - @Test def toMultimapExample4(): Unit = { - val o : Observable[String] = List("alice", "bob", "carol", "allen", "clarke").toObservable - val keySelector = (s: String) => s.head - val valueSelector = (s: String) => s.tail - val mapFactory = () => mutable.Map('d' -> mutable.ListBuffer("oug")) - val bufferFactory = (k: Char) => mutable.ListBuffer[String]() - val m = o.toMultimap(keySelector, valueSelector, mapFactory, bufferFactory) - println(m.toBlocking.single) - } - - @Test def containsExample(): Unit = { - val o1 = List(1, 2, 3).toObservable.contains(2) - assertTrue(o1.toBlocking.single) - - val o2 = List(1, 2, 3).toObservable.contains(4) - assertFalse(o2.toBlocking.single) - } - - @Test def repeatExample1(): Unit = { - val o : Observable[String] = List("alice", "bob", "carol").toObservable.repeat.take(6) - assertEquals(List("alice", "bob", "carol", "alice", "bob", "carol"), o.toBlocking.toList) - } - - @Test def repeatExample2(): Unit = { - val o : Observable[String] = List("alice", "bob", "carol").toObservable.repeat(2) - assertEquals(List("alice", "bob", "carol", "alice", "bob", "carol"), o.toBlocking.toList) - } - - @Test def retryExample1(): Unit = { - val o : Observable[String] = List("alice", "bob", "carol").toObservable - assertEquals(List("alice", "bob", "carol"), o.retry.toBlocking.toList) - } - - @Test def retryExample2(): Unit = { - val o : Observable[String] = List("alice", "bob", "carol").toObservable - assertEquals(List("alice", "bob", "carol"), o.retry(3).toBlocking.toList) - } - - @Test def retryExample3(): Unit = { - var isFirst = true - val o = Observable { - (subscriber: Subscriber[String]) => - if (isFirst) { - subscriber.onNext("alice") - subscriber.onError(new IOException("Oops")) - isFirst = false - } - else { - subscriber.onNext("bob") - subscriber.onError(new RuntimeException("Oops")) - } - } - o.retry { - (times, e) => e match { - case e: IOException => times <= 3 - case _ => false - } - }.subscribe(s => println(s), e => e.printStackTrace()) - } - - @Test def liftExample1(): Unit = { - // Add "No. " in front of each item - val o = List(1, 2, 3).toObservable.lift { - subscriber: Subscriber[String] => - Subscriber[Int]( - subscriber, - (v: Int) => subscriber.onNext("No. " + v), - e => subscriber.onError(e), - () => subscriber.onCompleted - ) - }.toBlocking.toList - println(o) - } - - @Test def liftExample2(): Unit = { - // Split the input Strings with " " - val splitStringsWithSpace = (subscriber: Subscriber[String]) => { - Subscriber[String]( - subscriber, - (v: String) => v.split(" ").foreach(subscriber.onNext(_)), - e => subscriber.onError(e), - () => subscriber.onCompleted - ) - } - - // Convert the input Strings to Chars - val stringsToChars = (subscriber: Subscriber[Char]) => { - Subscriber[String]( - subscriber, - (v: String) => v.foreach(subscriber.onNext(_)), - e => subscriber.onError(e), - () => subscriber.onCompleted - ) - } - - // Skip the first n items. If the length of source is less than n, throw an IllegalArgumentException - def skipWithException[T](n: Int) = (subscriber: Subscriber[T]) => { - var count = 0 - Subscriber[T]( - subscriber, - (v: T) => { - if (count >= n) subscriber.onNext(v) - count += 1 - }, - e => subscriber.onError(e), - () => if (count < n) subscriber.onError(new IllegalArgumentException("There is no enough items")) else subscriber.onCompleted - ) - } - - val o = List("RxJava – Reactive Extensions for the JVM").toObservable - .lift(splitStringsWithSpace) - .map(_.toLowerCase) - .lift(stringsToChars) - .filter(_.isLetter) - .lift(skipWithException(100)) - try { - o.toBlocking.toList - } - catch { - case e: IllegalArgumentException => println("IllegalArgumentException from skipWithException") - } - } - - @Test def multicastExample1(): Unit = { - val unshared = Observable.from(1 to 4) - val shared = unshared.multicast(Subject[Int]()) - shared.subscribe(n => println(s"subscriber 1 gets $n")) - shared.subscribe(n => println(s"subscriber 2 gets $n")) - shared.connect - } - - @Test def multicastExample2(): Unit = { - val unshared = Observable.from(1 to 4) - val shared = unshared.multicast(() => Subject[Int]())(o => o.map("No. " + _)) - shared.subscribe(n => println(s"subscriber 1 gets $n")) - shared.subscribe(n => println(s"subscriber 2 gets $n")) - } - - @Test def startWithExample(): Unit = { - val o1 = List(3, 4).toObservable - val o2 = 1 +: 2 +: o1 - assertEquals(List(1, 2, 3, 4), o2.toBlocking.toList) - } - - @Test def appendExample(): Unit = { - val o = List(1, 2).toObservable :+ 3 :+ 4 - assertEquals(List(1, 2, 3, 4), o.toBlocking.toList) - } - - @Test def sequenceEqualExampe(): Unit = { - val o1 = List(1, 2, 3).toObservable - val o2 = List(1, 2, 3).toObservable - val o3 = List(1, 2).toObservable - val o4 = List(1.0, 2.0, 3.0).toObservable - assertTrue(o1.sequenceEqual(o2).toBlocking.single) - assertFalse(o1.sequenceEqual(o3).toBlocking.single) - assertTrue(o1.sequenceEqual(o4).toBlocking.single) - } - - @Test def takeExample(): Unit = { - val o = (1 to 20).toObservable - .zip(Observable.interval(300 millis)) - .map(_._1) - .take(2 seconds) - println(o.toBlocking.toList) - } - - @Test def takeRightExample(): Unit = { - val o = (1 to 6).toObservable.takeRight(3) - assertEquals(List(4, 5, 6), o.toBlocking.toList) - } - - @Test def takeRightExample2(): Unit = { - val o = (1 to 10).toObservable - .zip(Observable.interval(100 millis)) - .map(_._1) - .takeRight(300 millis) - println(o.toBlocking.toList) - } - - @Test def takeRightExample3(): Unit = { - val o = (1 to 10).toObservable - .zip(Observable.interval(100 millis)) - .map(_._1) - .takeRight(2, 300 millis) - println(o.toBlocking.toList) - } - - @Test def timeIntervalExample(): Unit = { - val o = (1 to 10).toObservable - .zip(Observable.interval(100 millis)) - .map(_._1) - .timeInterval - println(o.toBlocking.toList) - } - - @Test def schedulerExample1(): Unit = { - val latch = new CountDownLatch(1) - val worker = IOScheduler().createWorker - worker.schedule { - println("Hello from Scheduler") - latch.countDown() - } - latch.await(5, TimeUnit.SECONDS) - } - - @Test def schedulerExample2(): Unit = { - val latch = new CountDownLatch(1) - val worker = IOScheduler().createWorker - worker.schedule(1 seconds) { - println("Hello from Scheduler after 1 second") - latch.countDown() - } - latch.await(5, TimeUnit.SECONDS) - } - - @Test def schedulerExample3(): Unit = { - val worker = IOScheduler().createWorker - var no = 1 - val subscription = worker.schedulePeriodically(initialDelay = 1 seconds, period = 100 millis) { - println(s"Hello(${no}) from Scheduler") - no += 1 - } - TimeUnit.SECONDS.sleep(2) - subscription.unsubscribe() - } - - @Test def schedulerExample4(): Unit = { - val worker = IOScheduler().createWorker - var no = 1 - def hello: Unit = { - println(s"Hello(${no}) from Scheduler") - no += 1 - worker.schedule(100 millis)(hello) - } - val subscription = worker.schedule(1 seconds)(hello) - TimeUnit.SECONDS.sleep(2) - subscription.unsubscribe() - } - - def createAHotObservable: Observable[String] = { - var first = true - Observable[String] { - subscriber => - if (first) { - subscriber.onNext("1st: First") - subscriber.onNext("1st: Last") - first = false - } - else { - subscriber.onNext("2nd: First") - subscriber.onNext("2nd: Last") - } - subscriber.onCompleted() - } - } - - @Test def withoutPublishLastExample() { - val hot = createAHotObservable - hot.takeRight(1).subscribe(n => println(s"subscriber 1 gets $n")) - hot.takeRight(1).subscribe(n => println(s"subscriber 2 gets $n")) - } - - @Test def publishLastExample() { - val hot = createAHotObservable - val o = hot.publishLast - o.subscribe(n => println(s"subscriber 1 gets $n")) - o.subscribe(n => println(s"subscriber 2 gets $n")) - o.connect - } - - @Test def publishLastExample2() { - val hot = createAHotObservable - val o = hot.publishLast(co => co ++ co) // "++" subscribes "co" twice - o.subscribe(n => println(s"subscriber gets $n")) - } - - @Test def unsubscribeOnExample() { - val o = Observable[String] { - subscriber => - subscriber.add(Subscription { - println("unsubscribe on " + Thread.currentThread().getName()) - }) - subscriber.onNext("RxScala") - subscriber.onCompleted() - } - o.unsubscribeOn(NewThreadScheduler()).subscribe(println(_)) - } - - @Test def parallelMergeExample() { - val o: Observable[Observable[Int]] = (1 to 100).toObservable.map(_ => (1 to 10).toObservable) - assertEquals(100, o.size.toBlocking.single) - assertEquals(1000, o.flatten.size.toBlocking.single) - - val o2: Observable[Observable[Int]] = o.parallelMerge(10, ComputationScheduler()) - assertEquals(10, o2.size.toBlocking.single) - assertEquals(1000, o2.flatten.size.toBlocking.single) - } - - @Test def debounceExample() { - val o = Observable.interval(100 millis).take(20).debounce { - n => - if (n % 2 == 0) { - Observable.interval(50 millis) - } - else { - Observable.interval(150 millis) - } - } - o.toBlocking.foreach(println(_)) - } - - @Test def flatMapExample() { - val o = Observable.items(10, 100) - o.flatMap(n => Observable.interval(200 millis).map(_ * n)) - .take(20) - .toBlocking.foreach(println) - } - - @Test def flatMapExample2() { - val o = Observable.items(10, 100) - val o1 = for (n <- o; - i <- Observable.interval(200 millis)) yield i * n - o1.take(20).toBlocking.foreach(println) - } - - @Test def flatMapExample3() { - val o = Observable[Int] { - subscriber => - subscriber.onNext(10) - subscriber.onNext(100) - subscriber.onError(new IOException("Oops")) - } - o.flatMap( - (n: Int) => Observable.interval(200 millis).map(_ * n), - e => Observable.interval(200 millis).map(_ * -1), - () => Observable.interval(200 millis).map(_ * 1000) - ).take(20) - .toBlocking.foreach(println) - } - - @Test def flatMapExample4() { - val o = Observable.items(10, 100) - o.flatMap( - (n: Int) => Observable.interval(200 millis).map(_ * n), - e => Observable.interval(200 millis).map(_ * -1), - () => Observable.interval(200 millis).map(_ * 1000) - ).take(20) - .toBlocking.foreach(println) - } - - @Test def flatMapExample5() { - val o = Observable.items(1, 10, 100, 1000) - o.flatMapWith(_ => Observable.interval(200 millis).take(5))(_ * _).toBlocking.foreach(println) - } - - @Test def flatMapIterableExample() { - val o = Observable.items(10, 100) - o.flatMapIterable(n => (1 to 20).map(_ * n)) - .toBlocking.foreach(println) - } - - @Test def flatMapIterableExample2() { - val o = Observable.items(1, 10, 100, 1000) - o.flatMapIterableWith(_=> (1 to 5))(_ * _).toBlocking.foreach(println) - } - - @Test def concatMapExample() { - val o = Observable.items(10, 100) - o.concatMap(n => Observable.interval(200 millis).map(_ * n).take(10)) - .take(20) - .toBlocking.foreach(println) - } - - @Test def onErrorResumeNextExample() { - val o = Observable { - (subscriber: Subscriber[Int]) => - subscriber.onNext(1) - subscriber.onNext(2) - subscriber.onError(new IOException("Oops")) - subscriber.onNext(3) - subscriber.onNext(4) - } - o.onErrorResumeNext(_ => Observable.items(10, 11, 12)).subscribe(println(_)) - } - - @Test def onErrorFlatMapExample() { - val o = Observable { - (subscriber: Subscriber[Int]) => - subscriber.onNext(1) - subscriber.onNext(2) - subscriber.onError(new IOException("Oops")) - subscriber.onNext(3) - subscriber.onNext(4) - } - o.onErrorFlatMap((_, _) => Observable.items(10, 11, 12)).subscribe(println(_)) - } - - @Test def onErrorFlatMapExample2() { - val o = Observable.items(4, 2, 0).map(16 / _).onErrorFlatMap { - (e, op) => op match { - case Some(v) if v == 0 => Observable.items(Int.MinValue) - case _ => Observable.empty - } - } - o.subscribe(println(_)) - } - - @Test def switchMapExample() { - val o = Observable.interval(300 millis).take(5).switchMap[String] { - n => Observable.interval(50 millis).take(10).map(i => s"Seq ${n}: ${i}") - } - o.toBlocking.foreach(println) - } - - @Test def joinExample() { - val o1 = Observable.interval(500 millis).map(n => "1: " + n) - val o2 = Observable.interval(100 millis).map(n => "2: " + n) - val o = o1.join(o2)(_ => Observable.timer(300 millis), _ => Observable.timer(200 millis), (_, _)) - o.take(10).toBlocking.foreach(println) - } - - @Test def groupJoinExample() { - val o1 = Observable.interval(500 millis).map(n => "1: " + n) - val o2 = Observable.interval(100 millis).map(n => "2: " + n) - val o = o1.groupJoin(o2)(_ => Observable.timer(300 millis), _ => Observable.timer(200 millis), (t1, t2) => (t1, t2.toSeq.toBlocking.single)) - o.take(3).toBlocking.foreach(println) - } - - @Test def pivotExample() { - val o1 = (1 to 20).toObservable.groupBy(i => if (i <= 10) "x" else "y").map { - case (t: String, o: Observable[Int]) => (t, o.groupBy(i => i % 2 == 0)) - } - println("o1:") - (for ((k1, o) <- o1; - (k2, vs) <- o; - v <- vs - ) yield (k1, k2, v)).subscribe(println(_)) - val o2 = o1.pivot - println("o2:") - (for ((k1, o) <- o2; - (k2, vs) <- o; - v <- vs - ) yield (k1, k2, v)).subscribe(println(_)) - } -} diff --git a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala b/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala deleted file mode 100644 index bf4afa9c7a..0000000000 --- a/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/TestSchedulerExample.scala +++ /dev/null @@ -1,65 +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.examples - -import scala.concurrent.duration.DurationInt -import scala.language.postfixOps -import org.junit.Test -import org.mockito.Matchers._ -import org.mockito.Mockito._ -import org.scalatest.junit.JUnitSuite -import rx.lang.scala._ -import rx.lang.scala.schedulers.TestScheduler -import rx.observers.TestObserver - -class TestSchedulerExample extends JUnitSuite { - - @Test def testInterval() { - val scheduler = TestScheduler() - // Use a Java Observer for Mockito - val observer = mock(classOf[rx.Observer[Long]]) - - val o = Observable.interval(1 second, scheduler) - - // Wrap Java Observer in Scala Observer, then subscribe - val sub = o.subscribe(Observer(new TestObserver(observer))) - - verify(observer, never).onNext(0L) - verify(observer, never).onCompleted() - verify(observer, never).onError(any(classOf[Throwable])) - - scheduler.advanceTimeTo(2 seconds) - - val inOrdr = inOrder(observer) - inOrdr.verify(observer, times(1)).onNext(0L) - inOrdr.verify(observer, times(1)).onNext(1L) - inOrdr.verify(observer, never).onNext(2L) - verify(observer, never).onCompleted() - verify(observer, never).onError(any(classOf[Throwable])) - - verify(observer, never).onNext(2L) - - sub.unsubscribe() - - scheduler.advanceTimeTo(4 seconds) - - // after unsubscription we expect no further events - verifyNoMoreInteractions(observer) - } - -} - - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala deleted file mode 100644 index 423ab119c9..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/ImplicitFunctionConversions.scala +++ /dev/null @@ -1,137 +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 java.lang.Exception -import java.{ lang => jlang } -import scala.language.implicitConversions -import scala.collection.Seq -import rx.functions._ -import rx.lang.scala.JavaConversions._ - - -/** - * These function conversions convert between Scala functions and Rx `Func`s and `Action`s. - * Most RxScala users won't need them, but they might be useful if one wants to use - * the `rx.Observable` directly instead of using `rx.lang.scala.Observable` or if one wants - * to use a Java library taking/returning `Func`s and `Action`s. - * This object only contains conversions between functions. For conversions between types, - * use [[rx.lang.scala.JavaConversions]]. - */ -object ImplicitFunctionConversions { - -// implicit def schedulerActionToFunc2[T](action: (Scheduler, T) => Subscription): Func2[rx.Scheduler, T, rx.Subscription] with Object {def call(s: rx.Scheduler, t: T): rx.Subscription} = -// new Func2[rx.Scheduler, T, rx.Subscription] { -// def call(s: rx.Scheduler, t: T): rx.Subscription = { -// action(rx.lang.scala.Scheduler(s), t).asJavaSubscription -// } -// } - - implicit def schedulerActionToFunc2[T](action: (Scheduler, T) => Subscription) = - new Func2[rx.Scheduler, T, rx.Subscription] { - def call(s: rx.Scheduler, t: T): rx.Subscription = { - action(Scheduler(s), t).asJavaSubscription - } - } - - implicit def scalaFunction1ToOnSubscribeFunc[T](f: rx.lang.scala.Observer[T] => Subscription) = - new rx.Observable.OnSubscribeFunc[T] { - def onSubscribe(obs: rx.Observer[_ >: T]): rx.Subscription = { - f(obs) - } - } - - implicit def scalaAction1ToOnSubscribe[T](f: Subscriber[T] => Unit) = - new rx.Observable.OnSubscribe[T] { - def call(s: rx.Subscriber[_ >: T]): Unit = { - f(s) - } - } - - implicit def scalaByNameParamToFunc0[B](param: => B): Func0[B] = - new Func0[B] { - def call(): B = param - } - - implicit def scalaFunction0ProducingUnitToAction0(f: (() => Unit)): Action0 = - new Action0 { - def call(): Unit = f() - } - - implicit def Action1toScalaFunction1ProducingUnit[A](f: Action1[A]): (A=>Unit) = { - a => f(a) - } - - implicit def scalaFunction1ProducingUnitToAction1[A](f: (A => Unit)): Action1[A] = - new Action1[A] { - def call(a: A): Unit = f(a) - } - - 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 - } - - implicit def scalaBooleanFunction2ToRxBooleanFunc1[A, B](f: ((A, B) => Boolean)): Func2[A, B, jlang.Boolean] = - new Func2[A, B, jlang.Boolean] { - def call(a: A, b: B): jlang.Boolean = f(a, b).booleanValue - } - - implicit def scalaFuncNToRxFuncN[R](f: Seq[java.lang.Object] => R): FuncN[R] = - new FuncN[R] { - def call(args: java.lang.Object*): R = f(args) - } - - 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 - } - - 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 - } - - 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]) - } - - 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) - } -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/JavaConversions.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/JavaConversions.scala deleted file mode 100644 index df86c08f80..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/JavaConversions.scala +++ /dev/null @@ -1,65 +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 - -/** - * These functions convert between RxScala types RxJava types. - * Pure Scala projects won't need them, but they will be useful for polyglot projects. - * This object only contains conversions between types. For conversions between functions, - * use [[rx.lang.scala.ImplicitFunctionConversions]]. - */ -object JavaConversions { - import language.implicitConversions - - implicit def toJavaNotification[T](s: Notification[T]): rx.Notification[_ <: T] = s.asJavaNotification - - implicit def toScalaNotification[T](s: rx.Notification[_ <: T]): Notification[T] = Notification(s) - - implicit def toJavaSubscription(s: Subscription): rx.Subscription = s.asJavaSubscription - - implicit def toScalaSubscription(s: rx.Subscription): Subscription = Subscription(s) - - implicit def toJavaSubscriber[T](s: Subscriber[T]): rx.Subscriber[_ >: T] = s.asJavaSubscriber - - implicit def toScalaSubscriber[T](s: rx.Subscriber[_ >: T]): Subscriber[T] = Subscriber(s) - - implicit def scalaSchedulerToJavaScheduler(s: Scheduler): rx.Scheduler = s.asJavaScheduler - implicit def javaSchedulerToScalaScheduler(s: rx.Scheduler): Scheduler = Scheduler(s) - - implicit def scalaWorkerToJavaWorker(s: Worker): rx.Scheduler.Worker = s.asJavaWorker - implicit def javaWorkerToScalaWorker(s: rx.Scheduler.Worker): Worker = Worker(s) - - - implicit def toJavaObserver[T](s: Observer[T]): rx.Observer[_ >: T] = s.asJavaObserver - - implicit def toScalaObserver[T](s: rx.Observer[_ >: T]): Observer[T] = Observer(s) - - implicit def toJavaObservable[T](s: Observable[T]): rx.Observable[_ <: T] = s.asJavaObservable - - implicit def toScalaObservable[T](observable: rx.Observable[_ <: T]): Observable[T] = { - new Observable[T]{ - val asJavaObservable = observable - } - } - - implicit def toJavaOperator[T, R](operator: Subscriber[R] => Subscriber[T]): rx.Observable.Operator[R, T] = { - new rx.Observable.Operator[R, T] { - override def call(subscriber: rx.Subscriber[_ >: R]): rx.Subscriber[_ >: T] = { - toJavaSubscriber[T](operator(toScalaSubscriber[R](subscriber))) - } - } - } -} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala deleted file mode 100644 index c9e8cd4822..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Notification.scala +++ /dev/null @@ -1,159 +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 - -/** - * Emitted by Observables returned by [[rx.lang.scala.Observable.materialize]]. - */ -sealed trait Notification[+T] { - private [scala] val asJavaNotification: rx.Notification[_ <: T] - - override def equals(that: Any): Boolean = that match { - case other: Notification[_] => asJavaNotification.equals(other.asJavaNotification) - case _ => false - } - override def hashCode(): Int = asJavaNotification.hashCode() - - /** - * Invokes the function corresponding to the notification. - * - * @param onNext - * The function to invoke for an [[rx.lang.scala.Notification.OnNext]] notification. - * @param onError - * The function to invoke for an [[rx.lang.scala.Notification.OnError]] notification. - * @param onCompleted - * The function to invoke for an [[rx.lang.scala.Notification.OnCompleted]] notification. - */ - def accept[R](onNext: T=>R, onError: Throwable=>R, onCompleted: ()=>R): R = { - this match { - case Notification.OnNext(value) => onNext(value) - case Notification.OnError(error) => onError(error) - case Notification.OnCompleted => onCompleted() - } - } - - def apply[R](onNext: T=>R, onError: Throwable=>R, onCompleted: ()=>R): R = - accept(onNext, onError, onCompleted) - - /** - * Invokes the observer corresponding to the notification - * - * @param observer - * The observer that to observe the notification - */ - def accept(observer: Observer[T]): Unit = { - this match { - case Notification.OnNext(value) => observer.onNext(value) - case Notification.OnError(error) => observer.onError(error) - case Notification.OnCompleted => observer.onCompleted() - } - } - - def apply(observer: Observer[T]): Unit = accept(observer) -} - -/** - * Provides pattern matching support and constructors for Notifications. - * - * Example: - * {{{ - * import Notification._ - * Observable(1, 2, 3).materialize.subscribe(n => n match { - * case OnNext(v) => println("Got value " + v) - * case OnCompleted() => println("Completed") - * case OnError(err) => println("Error: " + err.getMessage) - * }) - * }}} - */ -object Notification { - - private [scala] def apply[T](n: rx.Notification[_ <: T]): Notification[T] = n.getKind match { - case rx.Notification.Kind.OnNext => new OnNext(n) - case rx.Notification.Kind.OnCompleted => OnCompleted - case rx.Notification.Kind.OnError => new OnError(n) - } - - // OnNext, OnError, OnCompleted are not case classes because we don't want pattern matching - // to extract the rx.Notification - - object OnNext { - - /** - * Constructor for onNext notifications. - * - * @param value - * The item passed to the onNext method. - */ - def apply[T](value: T): Notification[T] = { - Notification(rx.Notification.createOnNext[T](value)) - } - - /** - * Extractor for onNext notifications. - * @param notification - * The [[rx.lang.scala.Notification]] to be destructed. - * @return - * The item contained in this notification. - */ - def unapply[U](notification: Notification[U]): Option[U] = notification match { - case onNext: OnNext[U] => Some(onNext.value) - case _ => None - } - } - - class OnNext[+T] private[scala] (val asJavaNotification: rx.Notification[_ <: T]) extends Notification[T] { - def value: T = asJavaNotification.getValue - override def toString = s"OnNext($value)" - } - - object OnError { - - /** - * Constructor for onError notifications. - * - * @param error - * The exception passed to the onNext method. - */ - def apply[T](error: Throwable): Notification[T] = { - Notification(rx.Notification.createOnError[T](error)) - } - - /** - * Destructor for onError notifications. - * - * @param notification - * The [[rx.lang.scala.Notification]] to be deconstructed - * @return - * The `java.lang.Throwable` value contained in this notification. - */ - def unapply[U](notification: Notification[U]): Option[Throwable] = notification match { - case onError: OnError[U] => Some(onError.error) - case _ => None - } - } - - class OnError[+T] private[scala] (val asJavaNotification: rx.Notification[_ <: T]) extends Notification[T] { - def error: Throwable = asJavaNotification.getThrowable - override def toString = s"OnError($error)" - } - - object OnCompleted extends Notification[Nothing] { - override def toString = "OnCompleted" - val asJavaNotification = rx.Notification.createOnCompleted[Nothing]() - } - -} - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala deleted file mode 100644 index 2157504074..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala +++ /dev/null @@ -1,4604 +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.functions.FuncN -import rx.lang.scala.observables.ConnectableObservable -import scala.concurrent.duration -import java.util -import collection.JavaConversions._ -import scala.collection.generic.CanBuildFrom -import scala.annotation.unchecked.uncheckedVariance -import scala.collection.{Iterable, Traversable, immutable} -import scala.collection.mutable.ArrayBuffer -import scala.language.higherKinds -import scala.reflect.ClassTag - - -/** - * The Observable interface that implements the Reactive Pattern. - * - * @define subscribeObserverMain - * Call this method to subscribe an [[rx.lang.scala.Observer]] for receiving - * items and notifications from the Observable. - * - * A typical implementation of `subscribe` does the following: - * - * It stores a reference to the Observer in a collection object, such as a `List[T]` object. - * - * It returns a reference to the [[rx.lang.scala.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 [[rx.lang.scala.Observer.onCompleted onCompleted]] method. - * - * An `Observable[T]` instance is responsible for accepting all subscriptions - * and notifying all Observers. Unless the documentation for a particular - * `Observable[T]` implementation indicates otherwise, Observers should make no - * assumptions about the order in which multiple Observers will receive their notifications. - * - * @define subscribeObserverParamObserver - * the observer - * @define subscribeObserverParamScheduler - * the [[rx.lang.scala.Scheduler]] on which Observers subscribe to the Observable - * - * @define subscribeSubscriberMain - * Call this method to subscribe an [[Subscriber]] for receiving items and notifications from the [[Observable]]. - * - * A typical implementation of `subscribe` does the following: - * - * It stores a reference to the Observer in a collection object, such as a `List[T]` object. - * - * It returns a reference to the [[rx.lang.scala.Subscription]] interface. This enables [[Subscriber]]s to - * unsubscribe, that is, to stop receiving items and notifications before the Observable stops - * sending them, which also invokes the Subscriber's [[rx.lang.scala.Observer.onCompleted onCompleted]] method. - * - * An [[Observable]] instance is responsible for accepting all subscriptions - * and notifying all [[Subscriber]]s. Unless the documentation for a particular - * [[Observable]] implementation indicates otherwise, [[Subscriber]]s should make no - * assumptions about the order in which multiple [[Subscriber]]s will receive their notifications. - * - * @define subscribeSubscriberParamObserver - * the [[Subscriber]] - * @define subscribeSubscriberParamScheduler - * the [[rx.lang.scala.Scheduler]] on which [[Subscriber]]s subscribe to the Observable - * - * @define subscribeAllReturn - * a [[rx.lang.scala.Subscription]] reference whose `unsubscribe` method can be called to stop receiving items - * before the Observable has finished sending them - * - * @define subscribeCallbacksMainWithNotifications - * Call this method to receive items and notifications from this observable. - * - * @define subscribeCallbacksMainNoNotifications - * Call this method to receive items from this observable. - * - * @define subscribeCallbacksParamOnNext - * this function will be called whenever the Observable emits an item - * @define subscribeCallbacksParamOnError - * this function will be called if an error occurs - * @define subscribeCallbacksParamOnComplete - * this function will be called when this Observable has finished emitting items - * @define subscribeCallbacksParamScheduler - * the scheduler to use - * - * @define debounceVsThrottle - * Information on debounce vs throttle: - * - [[http://drupalmotion.com/article/debounce-and-throttle-visual-explanation]] - * - [[http://unscriptable.com/2009/03/20/debouncing-javascript-methods/]] - * - [[http://www.illyriad.co.uk/blog/index.php/2011/09/javascript-dont-spam-your-server-debounce-and-throttle/]] - * - * - */ -trait Observable[+T] -{ - import scala.collection.JavaConverters._ - import scala.collection.Seq - import scala.concurrent.duration.{Duration, TimeUnit, MILLISECONDS} - import scala.collection.mutable - import rx.functions._ - import rx.lang.scala.observables.BlockingObservable - import ImplicitFunctionConversions._ - import JavaConversions._ - - private [scala] val asJavaObservable: rx.Observable[_ <: T] - - /** - * $subscribeObserverMain - * - * @return $subscribeAllReturn - */ - def subscribe(): Subscription = { - asJavaObservable.subscribe() - } - - /** - * $subscribeObserverMain - * - * @param observer $subscribeObserverParamObserver - * @return $subscribeAllReturn - */ - def subscribe(observer: Observer[T]): Subscription = { - asJavaObservable.subscribe(observer.asJavaObserver) - } - - /** - * $subscribeObserverMain - * - * @param observer $subscribeObserverParamObserver - * @return $subscribeAllReturn - */ - def apply(observer: Observer[T]): Subscription = subscribe(observer) - - /** - * $subscribeSubscriberMain - * - * @param subscriber $subscribeSubscriberParamObserver - * @return $subscribeAllReturn - */ - def subscribe(subscriber: Subscriber[T]): Subscription = { - // Add the casting to avoid compile error "ambiguous reference to overloaded definition" - val thisJava = asJavaObservable.asInstanceOf[rx.Observable[T]] - thisJava.subscribe(subscriber.asJavaSubscriber) - } - - /** - * Subscribe to Observable and invoke `OnSubscribe` function without any - * contract protection, error handling, unsubscribe, or execution hooks. - * - * This should only be used for implementing an `Operator` that requires nested subscriptions. - * - * Normal use should use [[Observable.subscribe]] which ensures the Rx contract and other functionality. - * - * @param subscriber - * @return [[Subscription]] which is the Subscriber passed in - * @since 0.17 - */ - def unsafeSubscribe(subscriber: Subscriber[T]): Subscription = { - asJavaObservable.unsafeSubscribe(subscriber.asJavaSubscriber) - } - - /** - * $subscribeSubscriberMain - * - * @param subscriber $subscribeSubscriberParamObserver - * @return $subscribeAllReturn - */ - def apply(subscriber: Subscriber[T]): Subscription = subscribe(subscriber) - - /** - * $subscribeCallbacksMainNoNotifications - * - * @param onNext $subscribeCallbacksParamOnNext - * @return $subscribeAllReturn - */ - def subscribe(onNext: T => Unit): Subscription = { - asJavaObservable.subscribe(scalaFunction1ProducingUnitToAction1(onNext)) - } - - /** - * $subscribeCallbacksMainWithNotifications - * - * @param onNext $subscribeCallbacksParamOnNext - * @param onError $subscribeCallbacksParamOnError - * @return $subscribeAllReturn - */ - def subscribe(onNext: T => Unit, onError: Throwable => Unit): Subscription = { - asJavaObservable.subscribe( - scalaFunction1ProducingUnitToAction1(onNext), - scalaFunction1ProducingUnitToAction1(onError) - ) - } - - /** - * $subscribeCallbacksMainWithNotifications - * - * @param onNext $subscribeCallbacksParamOnNext - * @param onError $subscribeCallbacksParamOnError - * @param onCompleted $subscribeCallbacksParamOnComplete - * @return $subscribeAllReturn - */ - def subscribe(onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit): Subscription = { - asJavaObservable.subscribe( - scalaFunction1ProducingUnitToAction1(onNext), - scalaFunction1ProducingUnitToAction1(onError), - scalaFunction0ProducingUnitToAction0(onCompleted) - ) - } - - /** - * Returns a pair of a start function and an [[rx.lang.scala.Observable]] that upon calling the start function causes the source Observable to - * push results into the specified subject. - * - * @param subject - * the `rx.lang.scala.subjects.Subject` to push source items into - * @return a pair of a start function and an [[rx.lang.scala.Observable]] such that when the start function - * is called, the Observable starts to push results into the specified Subject - */ - def multicast[R >: T](subject: rx.lang.scala.Subject[R]): ConnectableObservable[R] = { - val s: rx.subjects.Subject[_ >: T, _<: R] = subject.asJavaSubject - new ConnectableObservable[R](asJavaObservable.multicast(s)) - } - - /** - * Returns an Observable that emits items produced by multicasting the source Observable within a selector function. - * - * @param subjectFactory the `Subject` factory - * @param selector the selector function, which can use the multicasted source Observable subject to the policies - * enforced by the created `Subject` - * @return an Observable that emits the items produced by multicasting the source Observable within a selector function - */ - def multicast[R >: T, U](subjectFactory: () => rx.lang.scala.Subject[R])(selector: Observable[R] => Observable[U]): Observable[U] = { - val subjectFactoryJava: Func0[rx.subjects.Subject[_ >: T, _ <: R]] = () => subjectFactory().asJavaSubject - val selectorJava: Func1[rx.Observable[R], rx.Observable[U]] = - (jo: rx.Observable[R]) => selector(toScalaObservable[R](jo)).asJavaObservable.asInstanceOf[rx.Observable[U]] - toScalaObservable[U](asJavaObservable.multicast[R, U](subjectFactoryJava, selectorJava)) - } - - /** - * Returns an Observable that first emits the items emitted by `this`, and then `elem`. - * - * @param elem the item to be appended - * @return an Observable that first emits the items emitted by `this`, and then `elem`. - */ - def :+[U >: T](elem: U): Observable[U] = { - this ++ Observable.items(elem) - } - - /** - * Returns an Observable that first emits the items emitted by `this`, and then the items emitted - * by `that`. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/concat.png"> - * - * @param that - * an Observable to be appended - * @return an Observable that emits items that are the result of combining the items emitted by - * this and that, one after the other - */ - def ++[U >: T](that: Observable[U]): Observable[U] = { - val o1: rx.Observable[_ <: U] = this.asJavaObservable - val o2: rx.Observable[_ <: U] = that.asJavaObservable - toScalaObservable(rx.Observable.concat(o1, o2)) - } - - /** - * Returns an Observable that emits a specified item before it begins to emit items emitted by the source Observable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/startWith.png"> - * - * @param elem the item to emit - * @return an Observable that emits the specified item before it begins to emit items emitted by the source Observable - */ - def +:[U >: T](elem: U): Observable[U] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - toScalaObservable(thisJava.startWith(elem)) - } - - /** - * Returns an Observable that emits the items emitted by several Observables, one after the - * other. - * - * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, - * otherwise you'll get a compilation error. - * - * @usecase def concat[U]: Observable[U] - */ - def concat[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { - val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable - val o5 = rx.Observable.concat[U](o4) - toScalaObservable[U](o5) - } - - /** - * Returns a new Observable that emits items resulting from applying a function that you supply to each item - * emitted by the source Observable, where that function returns an Observable, and then emitting the items - * that result from concatinating those resulting Observables. - * - * <img width="640" height="305" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/concatMap.png"> - * - * @param f 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 concatinating the Observables obtained from this transformation - */ - def concatMap[R](f: T => Observable[R]): Observable[R] = { - toScalaObservable[R](asJavaObservable.concatMap[R](new Func1[T, rx.Observable[_ <: R]] { - def call(t1: T): rx.Observable[_ <: R] = { - f(t1).asJavaObservable - } - })) - } - - /** - * Wraps this Observable in another Observable that ensures that the resulting - * Observable is chronologically well-behaved. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/synchronize.png"> - * - * A well-behaved Observable does not interleave its invocations of the [[rx.lang.scala.Observer.onNext onNext]], [[rx.lang.scala.Observer.onCompleted onCompleted]], and [[rx.lang.scala.Observer.onError onError]] methods of - * its [[rx.lang.scala.Observer]]s; it invokes `onCompleted` or `onError` only once; and it never invokes `onNext` after invoking either `onCompleted` or `onError`. - * [[Observable.serialize serialize]] enforces this, and the Observable it returns invokes `onNext` and `onCompleted` or `onError` synchronously. - * - * @return an Observable that is a chronologically well-behaved version of the source - * Observable, and that synchronously notifies its [[rx.lang.scala.Observer]]s - */ - def serialize: Observable[T] = { - toScalaObservable[T](asJavaObservable.serialize) - } - - /** - * Wraps each item emitted by a source Observable in a timestamped tuple. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/timestamp.png"> - * - * @return an Observable that emits timestamped items from the source Observable - */ - def timestamp: Observable[(Long, T)] = { - toScalaObservable[rx.schedulers.Timestamped[_ <: T]](asJavaObservable.timestamp()) - .map((t: rx.schedulers.Timestamped[_ <: T]) => (t.getTimestampMillis, t.getValue)) - } - - /** - * Wraps each item emitted by a source Observable in a timestamped tuple - * with timestamps provided by the given Scheduler. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timestamp.s.png"> - * - * @param scheduler [[rx.lang.scala.Scheduler]] to use as a time source. - * @return an Observable that emits timestamped items from the source - * Observable with timestamps provided by the given Scheduler - */ - def timestamp(scheduler: Scheduler): Observable[(Long, T)] = { - toScalaObservable[rx.schedulers.Timestamped[_ <: T]](asJavaObservable.timestamp(scheduler)) - .map((t: rx.schedulers.Timestamped[_ <: T]) => (t.getTimestampMillis, t.getValue)) - } - - /** - * Returns an Observable formed from this Observable and another Observable by combining - * corresponding elements in pairs. - * The number of `onNext` invocations of the resulting `Observable[(T, U)]` - * is the minumum of the number of `onNext` invocations of `this` and `that`. - */ - def zip[U](that: Observable[U]): Observable[(T, U)] = { - zipWith(that)((_, _)) - } - - /** - * Returns an Observable formed from `this` Observable and `other` Iterable by combining - * corresponding elements in pairs. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/zip.i.png"> - * <p> - * Note that the `other` Iterable is evaluated as items are observed from the source Observable; it is - * not pre-consumed. This allows you to zip infinite streams on either side. - * - * @param that the Iterable sequence - * @return an Observable that pairs up values from the source Observable and the `other` Iterable. - */ - def zip[U](that: Iterable[U]): Observable[(T, U)] = { - zipWith(that)((_, _)) - } - - /** - * Returns an Observable that emits items that are the result of applying a specified function to pairs of - * values, one each from the source Observable and a specified Iterable sequence. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/zip.i.png"> - * <p> - * Note that the `other` Iterable is evaluated as items are observed from the source Observable; it is - * not pre-consumed. This allows you to zip infinite streams on either side. - * - * @param that the Iterable sequence - * @param selector a function that combines the pairs of items from the Observable and the Iterable to generate - * the items to be emitted by the resulting Observable - * @return an Observable that pairs up values from the source Observable and the `other` Iterable - * sequence and emits the results of `selector` applied to these pairs - */ - def zipWith[U, R](that: Iterable[U])(selector: (T, U) => R): Observable[R] = { - val thisJava = asJavaObservable.asInstanceOf[rx.Observable[T]] - toScalaObservable[R](thisJava.zip(that.asJava, selector)) - } - - /** - * Returns an Observable formed from this Observable and another Observable by combining - * corresponding elements using the selector function. - * The number of `onNext` invocations of the resulting `Observable[(T, U)]` - * is the minumum of the number of `onNext` invocations of `this` and `that`. - */ - def zipWith[U, R](that: Observable[U])(selector: (T, U) => R): Observable[R] = { - toScalaObservable[R](rx.Observable.zip[T, U, R](this.asJavaObservable, that.asJavaObservable, selector)) - } - - /** - * Zips this Observable with its indices. - * - * @return An Observable emitting pairs consisting of all elements of this Observable paired with - * their index. Indices start at 0. - */ - def zipWithIndex: Observable[(T, Int)] = { - zip(0 until Int.MaxValue) - } - - /** - * Creates an Observable which produces buffers of collected values. - * - * This Observable produces buffers. Buffers are created when the specified `openings` - * Observable produces an object. That object is used to construct an Observable to emit buffers, feeding it into `closings` function. - * Buffers are emitted when the created Observable produces an object. - * - * @param openings - * The [[rx.lang.scala.Observable]] which, when it produces an object, will cause - * another buffer to be created. - * @param closings - * The function which is used to produce an [[rx.lang.scala.Observable]] for every buffer created. - * When this [[rx.lang.scala.Observable]] produces an object, the associated buffer - * is emitted. - * @return - * An [[rx.lang.scala.Observable]] which produces buffers which are created and emitted when the specified [[rx.lang.scala.Observable]]s publish certain objects. - */ - def slidingBuffer[Opening](openings: Observable[Opening])(closings: Opening => Observable[Any]): Observable[Seq[T]] = { - val opening: rx.Observable[_ <: Opening] = openings.asJavaObservable - val closing: Func1[_ >: Opening, _ <: rx.Observable[_ <: Any]] = (o: Opening) => closings(o).asJavaObservable - val jObs: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer[Opening, Any](opening, closing) - Observable.jObsOfListToScObsOfSeq(jObs.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - - /** - * 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 [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers containing at most - * `count` produced values. - */ - def tumblingBuffer(count: Int): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(count) - Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - - /** - * 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 `buffer(int)`. - * @return - * An [[rx.lang.scala.Observable]] which produces buffers every `skip` values containing at most - * `count` produced values. - */ - def slidingBuffer(count: Int, skip: Int): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(count, skip) - Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - - /** - * 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. - * @return - * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers with a fixed duration. - */ - def tumblingBuffer(timespan: Duration): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit) - Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - - /** - * 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 scheduler - * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a buffer. - * @return - * An [[rx.lang.scala.Observable]] which produces connected non-overlapping buffers with a fixed duration. - */ - def tumblingBuffer(timespan: Duration, scheduler: Scheduler): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit, scheduler) - Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - - /** - * 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 timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param count - * The maximum size of each buffer before it should be emitted. - * @return - * An [[rx.lang.scala.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). - */ - def tumblingBuffer(timespan: Duration, count: Int): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit, count) - Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - - /** - * 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 timespan - * The period of time each buffer is collecting values before it should be emitted, and - * replaced with a new buffer. - * @param count - * The maximum size of each buffer before it should be emitted. - * @param scheduler - * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a buffer. - * @return - * An [[rx.lang.scala.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). - */ - def tumblingBuffer(timespan: Duration, count: Int, scheduler: Scheduler): Observable[Seq[T]] = { - val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(timespan.length, timespan.unit, count, scheduler) - Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - - /** - * 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 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. - * @return - * An [[rx.lang.scala.Observable]] which produces new buffers periodically, and these are emitted after - * a fixed timespan has elapsed. - */ - def slidingBuffer(timespan: Duration, timeshift: Duration): Observable[Seq[T]] = { - val span: Long = timespan.length - val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) - val unit: TimeUnit = timespan.unit - val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(span, shift, unit) - Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - - /** - * 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 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 scheduler - * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a buffer. - * @return - * An [[rx.lang.scala.Observable]] which produces new buffers periodically, and these are emitted after - * a fixed timespan has elapsed. - */ - def slidingBuffer(timespan: Duration, timeshift: Duration, scheduler: Scheduler): Observable[Seq[T]] = { - val span: Long = timespan.length - val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) - val unit: TimeUnit = timespan.unit - val oJava: rx.Observable[_ <: java.util.List[_]] = asJavaObservable.buffer(span, shift, unit, scheduler) - Observable.jObsOfListToScObsOfSeq(oJava.asInstanceOf[rx.Observable[_ <: java.util.List[T]]]) - } - - /** - * Returns an Observable that emits non-overlapping buffered items from the source Observable each time the - * specified boundary Observable emits an item. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/buffer8.png"> - * <p> - * Completion of either the source or the boundary Observable causes the returned Observable to emit the - * latest buffer and complete. - * - * @param boundary the boundary Observable. Note: This is a by-name parameter, - * so it is only evaluated when someone subscribes to the returned Observable. - * @return an Observable that emits buffered items from the source Observable when the boundary Observable - * emits an item - */ - def tumblingBuffer(boundary: => Observable[Any]): Observable[Seq[T]] = { - val f = new Func0[rx.Observable[_ <: Any]]() { - override def call(): rx.Observable[_ <: Any] = boundary.asJavaObservable - } - toScalaObservable(asJavaObservable.buffer[Any](f)).map(_.asScala) - } - - /** - * Returns an Observable that emits non-overlapping buffered items from the source Observable each time the - * specified boundary Observable emits an item. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/buffer8.png"> - * <p> - * Completion of either the source or the boundary Observable causes the returned Observable to emit the - * latest buffer and complete. - * - * @param boundary the boundary Observable - * @param initialCapacity the initial capacity of each buffer chunk - * @return an Observable that emits buffered items from the source Observable when the boundary Observable - * emits an item - */ - def tumblingBuffer(boundary: Observable[Any], initialCapacity: Int): Observable[Seq[T]] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[T]] - toScalaObservable(thisJava.buffer(boundary.asJavaObservable, initialCapacity)).map(_.asScala) - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows. The boundary of each window is determined by the items emitted from a specified - * boundary-governing Observable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/window8.png" /> - * - * @param boundary an Observable whose emitted items close and open windows. Note: This is a by-name parameter, - * so it is only evaluated when someone subscribes to the returned Observable. - * @return An Observable which produces connected non-overlapping windows. The boundary of each window is - * determined by the items emitted from a specified boundary-governing Observable. - */ - def tumbling(boundary: => Observable[Any]): Observable[Observable[T]] = { - val func = new Func0[rx.Observable[_ <: Any]]() { - override def call(): rx.Observable[_ <: Any] = boundary.asJavaObservable - } - val jo: rx.Observable[_ <: rx.Observable[_ <: T]] = asJavaObservable.window[Any](func) - toScalaObservable(jo).map(toScalaObservable[T](_)) - } - - /** - * Creates an Observable which produces windows of collected values. Chunks are created when the specified `openings` - * Observable produces an object. That object is used to construct an Observable to emit windows, feeding it into `closings` function. - * Windows are emitted when the created Observable produces an object. - * - * @param openings - * The [[rx.lang.scala.Observable]] which when it produces an object, will cause - * another window to be created. - * @param closings - * The function which is used to produce an [[rx.lang.scala.Observable]] for every window created. - * When this [[rx.lang.scala.Observable]] produces an object, the associated window - * is emitted. - * @return - * An [[rx.lang.scala.Observable]] which produces windows which are created and emitted when the specified [[rx.lang.scala.Observable]]s publish certain objects. - */ - def sliding[Opening](openings: Observable[Opening])(closings: Opening => Observable[Any]) = { - Observable.jObsOfJObsToScObsOfScObs( - asJavaObservable.window[Opening, Any](openings.asJavaObservable, (op: Opening) => closings(op).asJavaObservable)) - : Observable[Observable[T]] // SI-7818 - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each containing `count` elements. When the source Observable completes or - * encounters an error, the current window is emitted, and the event is propagated. - * - * @param count - * The maximum size of each window before it should be emitted. - * @return - * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows containing at most - * `count` produced values. - */ - def tumbling(count: Int): Observable[Observable[T]] = { - // this unnecessary ascription is needed because of this bug (without, compiler crashes): - // https://issues.scala-lang.org/browse/SI-7818 - Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(count)) : Observable[Observable[T]] - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces windows every - * `skip` values, each containing `count` elements. When the source Observable completes or encounters an error, - * the current window is emitted and the event is propagated. - * - * @param count - * The maximum size of each window before it should be emitted. - * @param skip - * How many produced values need to be skipped before starting a new window. Note that when `skip` and - * `count` are equal that this is the same operation as `window(int)`. - * @return - * An [[rx.lang.scala.Observable]] which produces windows every `skip` values containing at most - * `count` produced values. - */ - def sliding(count: Int, skip: Int): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(count, skip)) - : Observable[Observable[T]] // SI-7818 - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the `timespan` argument. When the source - * Observable completes or encounters an error, the current window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @return - * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows with a fixed duration. - */ - def tumbling(timespan: Duration): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit)) - : Observable[Observable[T]] // SI-7818 - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, each of a fixed duration specified by the `timespan` argument. When the source - * Observable completes or encounters an error, the current window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param scheduler - * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a window. - * @return - * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows with a fixed duration. - */ - def tumbling(timespan: Duration, scheduler: Scheduler): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit, scheduler)) - : Observable[Observable[T]] // SI-7818 - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, 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 window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param count - * The maximum size of each window before it should be emitted. - * @return - * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows which are emitted after - * a fixed duration or when the window has reached maximum capacity (which ever occurs first). - */ - def tumbling(timespan: Duration, count: Int): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit, count)) - : Observable[Observable[T]] // SI-7818 - } - - /** - * Creates an Observable which produces windows of collected values. This Observable produces connected - * non-overlapping windows, 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 window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted, and - * replaced with a new window. - * @param count - * The maximum size of each window before it should be emitted. - * @param scheduler - * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a window. - * @return - * An [[rx.lang.scala.Observable]] which produces connected non-overlapping windows which are emitted after - * a fixed duration or when the window has reached maximum capacity (which ever occurs first). - */ - def tumbling(timespan: Duration, count: Int, scheduler: Scheduler): Observable[Observable[T]] = { - Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(timespan.length, timespan.unit, count, scheduler)) - : Observable[Observable[T]] // SI-7818 - } - - /** - * Creates an Observable which produces windows of collected values. This Observable starts a new window - * periodically, which is determined by the `timeshift` argument. Each window is emitted after a fixed timespan - * specified by the `timespan` argument. When the source Observable completes or encounters an error, the - * current window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new window will be created. - * @return - * An [[rx.lang.scala.Observable]] which produces new windows periodically, and these are emitted after - * a fixed timespan has elapsed. - */ - def sliding(timespan: Duration, timeshift: Duration): Observable[Observable[T]] = { - val span: Long = timespan.length - val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) - val unit: TimeUnit = timespan.unit - Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(span, shift, unit)) - : Observable[Observable[T]] // SI-7818 - } - - /** - * Creates an Observable which produces windows of collected values. This Observable starts a new window - * periodically, which is determined by the `timeshift` argument. Each window is emitted after a fixed timespan - * specified by the `timespan` argument. When the source Observable completes or encounters an error, the - * current window is emitted and the event is propagated. - * - * @param timespan - * The period of time each window is collecting values before it should be emitted. - * @param timeshift - * The period of time after which a new window will be created. - * @param scheduler - * The [[rx.lang.scala.Scheduler]] to use when determining the end and start of a window. - * @return - * An [[rx.lang.scala.Observable]] which produces new windows periodically, and these are emitted after - * a fixed timespan has elapsed. - */ - def sliding(timespan: Duration, timeshift: Duration, scheduler: Scheduler): Observable[Observable[T]] = { - val span: Long = timespan.length - val shift: Long = timespan.unit.convert(timeshift.length, timeshift.unit) - val unit: TimeUnit = timespan.unit - Observable.jObsOfJObsToScObsOfScObs(asJavaObservable.window(span, shift, unit, scheduler)) - : Observable[Observable[T]] // SI-7818 - } - - /** - * Returns an Observable which only emits those items for which a given predicate holds. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/filter.png"> - * - * @param predicate - * a function that evaluates the items emitted by the source Observable, returning `true` if they pass the filter - * @return an Observable that emits only those items in the original Observable that the filter - * evaluates as `true` - */ - def filter(predicate: T => Boolean): Observable[T] = { - toScalaObservable[T](asJavaObservable.filter(predicate)) - } - - /** - * Registers an function to be called when this Observable invokes [[rx.lang.scala.Observer.onCompleted onCompleted]] or [[rx.lang.scala.Observer.onError onError]]. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/finallyDo.png"> - * - * @param action - * an function to be invoked when the source Observable finishes - * @return an Observable that emits the same items as the source Observable, then invokes the function - */ - def finallyDo(action: => Unit): Observable[T] = { - toScalaObservable[T](asJavaObservable.finallyDo(() => 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. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/flatMap.png"> - * - * @param f - * 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. - */ - def flatMap[R](f: T => Observable[R]): Observable[R] = { - toScalaObservable[R](asJavaObservable.flatMap[R](new Func1[T, rx.Observable[_ <: R]]{ - def call(t1: T): rx.Observable[_ <: R] = { f(t1).asJavaObservable } - })) - } - - /** - * Returns an Observable that applies a function to each item emitted or notification raised by the source - * Observable and then flattens the Observables returned from these functions and emits the resulting items. - * - * <img width="640" height="410" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/mergeMap.nce.png"> - * - * @tparam R the result type - * @param onNext a function that returns an Observable to merge for each item emitted by the source Observable - * @param onError a function that returns an Observable to merge for an onError notification from the source - * Observable - * @param onCompleted a function that returns an Observable to merge for an onCompleted notification from the source - * Observable - * @return an Observable that emits the results of merging the Observables returned from applying the - * specified functions to the emissions and notifications of the source Observable - */ - def flatMap[R](onNext: T => Observable[R], onError: Throwable => Observable[R], onCompleted: () => Observable[R]): Observable[R] = { - val jOnNext = new Func1[T, rx.Observable[_ <: R]] { - override def call(t: T): rx.Observable[_ <: R] = onNext(t).asJavaObservable - } - val jOnError = new Func1[Throwable, rx.Observable[_ <: R]] { - override def call(e: Throwable): rx.Observable[_ <: R] = onError(e).asJavaObservable - } - val jOnCompleted = new Func0[rx.Observable[_ <: R]] { - override def call(): rx.Observable[_ <: R] = onCompleted().asJavaObservable - } - toScalaObservable[R](asJavaObservable.mergeMap[R](jOnNext, jOnError, jOnCompleted)) - } - - /** - * Returns an Observable that emits the results of a specified function to the pair of values emitted by the - * source Observable and a specified collection Observable. - * - * <img width="640" height="390" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/mergeMap.r.png"> - * - * @tparam U the type of items emitted by the collection Observable - * @tparam R the type of items emitted by the resulting Observable - * @param collectionSelector a function that returns an Observable for each item emitted by the source Observable - * @param resultSelector a function that combines one item emitted by each of the source and collection Observables and - * returns an item to be emitted by the resulting Observable - * @return an Observable that emits the results of applying a function to a pair of values emitted by the - * source Observable and the collection Observable - */ - def flatMapWith[U, R](collectionSelector: T => Observable[U])(resultSelector: (T, U) => R): Observable[R] = { - val jCollectionSelector = new Func1[T, rx.Observable[_ <: U]] { - override def call(t: T): rx.Observable[_ <: U] = collectionSelector(t).asJavaObservable - } - toScalaObservable[R](asJavaObservable.mergeMap[U, R](jCollectionSelector, resultSelector)) - } - - /** - * Returns an Observable that merges each item emitted by the source Observable with the values in an - * Iterable corresponding to that item that is generated by a selector. - * - * <img width="640" height="310" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/mergeMapIterable.png"> - * - * @tparam R the type of item emitted by the resulting Observable - * @param collectionSelector a function that returns an Iterable sequence of values for when given an item emitted by the - * source Observable - * @return an Observable that emits the results of merging the items emitted by the source Observable with - * the values in the Iterables corresponding to those items, as generated by `collectionSelector` - */ - def flatMapIterable[R](collectionSelector: T => Iterable[R]): Observable[R] = { - val jCollectionSelector = new Func1[T, java.lang.Iterable[_ <: R]] { - override def call(t: T): java.lang.Iterable[_ <: R] = collectionSelector(t).asJava - } - toScalaObservable[R](asJavaObservable.mergeMapIterable[R](jCollectionSelector)) - } - - /** - * Returns an Observable that emits the results of applying a function to the pair of values from the source - * Observable and an Iterable corresponding to that item that is generated by a selector. - * - * <img width="640" height="390" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/mergeMapIterable.r.png"> - * - * @tparam U the collection element type - * @tparam R the type of item emited by the resulting Observable - * @param collectionSelector a function that returns an Iterable sequence of values for each item emitted by the source - * Observable - * @param resultSelector a function that returns an item based on the item emitted by the source Observable and the - * Iterable returned for that item by the `collectionSelector` - * @return an Observable that emits the items returned by `resultSelector` for each item in the source Observable - */ - def flatMapIterableWith[U, R](collectionSelector: T => Iterable[U])(resultSelector: (T, U) => R): Observable[R] = { - val jCollectionSelector = new Func1[T, java.lang.Iterable[_ <: U]] { - override def call(t: T): java.lang.Iterable[_ <: U] = collectionSelector(t).asJava - } - toScalaObservable[R](asJavaObservable.mergeMapIterable[U, R](jCollectionSelector, resultSelector)) - } - - /** - * Returns an Observable that applies the given function to each item emitted by an - * Observable and emits the result. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/map.png"> - * - * @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 - */ - def map[R](func: T => R): Observable[R] = { - toScalaObservable[R](asJavaObservable.map[R](new Func1[T,R] { - def call(t1: T): R = func(t1) - })) - } - - /** - * Turns all of the notifications from a source Observable into [[rx.lang.scala.Observer.onNext onNext]] emissions, - * and marks them with their original notification types within [[rx.lang.scala.Notification]] objects. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/materialize.png"> - * - * @return an Observable whose items are the result of materializing the items and - * notifications of the source Observable - */ - def materialize: Observable[Notification[T]] = { - toScalaObservable[rx.Notification[_ <: T]](asJavaObservable.materialize()).map(Notification(_)) - } - - /** - * Asynchronously subscribes and unsubscribes Observers on the specified [[rx.lang.scala.Scheduler]]. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/subscribeOn.png"> - * - * @param scheduler - * the [[rx.lang.scala.Scheduler]] to perform subscription and unsubscription actions on - * @return the source Observable modified so that its subscriptions and unsubscriptions happen - * on the specified [[rx.lang.scala.Scheduler]] - */ - def subscribeOn(scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.subscribeOn(scheduler)) - } - - /** - * Asynchronously unsubscribes on the specified [[Scheduler]]. - * - * @param scheduler the [[Scheduler]] to perform subscription and unsubscription actions on - * @return the source Observable modified so that its unsubscriptions happen on the specified [[Scheduler]] - * @since 0.17 - */ - def unsubscribeOn(scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.unsubscribeOn(scheduler)) - } - - /** - * Asynchronously notify [[rx.lang.scala.Observer]]s on the specified [[rx.lang.scala.Scheduler]]. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/observeOn.png"> - * - * @param scheduler - * the [[rx.lang.scala.Scheduler]] to notify [[rx.lang.scala.Observer]]s on - * @return the source Observable modified so that its [[rx.lang.scala.Observer]]s are notified on the - * specified [[rx.lang.scala.Scheduler]] - */ - def observeOn(scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.observeOn(scheduler)) - } - - /** - * Returns an Observable that reverses the effect of [[rx.lang.scala.Observable.materialize]] by - * transforming the [[rx.lang.scala.Notification]] objects emitted by the source Observable into the items - * or notifications they represent. - * - * This operation is only available if `this` is of type `Observable[Notification[U]]` for some `U`, - * otherwise you will get a compilation error. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/dematerialize.png"> - * - * @return an Observable that emits the items and notifications embedded in the [[rx.lang.scala.Notification]] objects emitted by the source Observable - * - * @usecase def dematerialize[U]: Observable[U] - * @inheritdoc - * - */ - // with =:= it does not work, why? - def dematerialize[U](implicit evidence: Observable[T] <:< Observable[Notification[U]]): Observable[U] = { - val o1: Observable[Notification[U]] = this - val o2: Observable[rx.Notification[_ <: U]] = o1.map(_.asJavaNotification) - val o3 = o2.asJavaObservable.dematerialize[U]() - toScalaObservable[U](o3) - } - - /** - * Instruct an Observable to pass control to another Observable rather than invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/onErrorResumeNext.png"> - * - * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's - * `onError` method, and then quits without invoking any more of its Observer's - * methods. The `onErrorResumeNext` method changes this behavior. If you pass a - * 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 - * [[rx.lang.scala.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 - */ - def onErrorResumeNext[U >: T](resumeFunction: Throwable => Observable[U]): Observable[U] = { - val f: Func1[Throwable, rx.Observable[_ <: U]] = (t: Throwable) => resumeFunction(t).asJavaObservable - val f2 = f.asInstanceOf[Func1[Throwable, rx.Observable[Nothing]]] - toScalaObservable[U](asJavaObservable.onErrorResumeNext(f2)) - } - - /** - * Instruct an Observable to pass control to another Observable rather than invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/onErrorResumeNext.png"> - * - * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's - * `onError` method, and then quits without invoking any more of its Observer's - * methods. The `onErrorResumeNext` method changes this behavior. If you pass - * 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 [[rx.lang.scala.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 - */ - def onErrorResumeNext[U >: T](resumeSequence: Observable[U]): Observable[U] = { - val rSeq1: rx.Observable[_ <: U] = resumeSequence.asJavaObservable - val rSeq2: rx.Observable[Nothing] = rSeq1.asInstanceOf[rx.Observable[Nothing]] - toScalaObservable[U](asJavaObservable.onErrorResumeNext(rSeq2)) - } - - /** - * Instruct an Observable to pass control to another Observable rather than invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error of type `java.lang.Exception`. - * - * This differs from `Observable.onErrorResumeNext` in that this one does not handle `java.lang.Throwable` or `java.lang.Error` but lets those continue through. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/onErrorResumeNext.png"> - * - * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[rx.lang.scala.Observer]], the Observable invokes its Observer's - * `onError` method, and then quits without invoking any more of its Observer's - * methods. The `onErrorResumeNext` method changes this behavior. If you pass - * 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 [[rx.lang.scala.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 - */ - def onExceptionResumeNext[U >: T](resumeSequence: Observable[U]): Observable[U] = { - val rSeq1: rx.Observable[_ <: U] = resumeSequence.asJavaObservable - val rSeq2: rx.Observable[Nothing] = rSeq1.asInstanceOf[rx.Observable[Nothing]] - toScalaObservable[U](asJavaObservable.onExceptionResumeNext(rSeq2)) - } - - /** - * Instruct an Observable to emit an item (returned by a specified function) rather than - * invoking [[rx.lang.scala.Observer.onError onError]] if it encounters an error. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/onErrorReturn.png"> - * - * By default, when an Observable encounters an error that prevents it from emitting the - * expected item to its [[rx.lang.scala.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` method, it will instead pass the return value of - * `resumeFunction` to the Observer's [[rx.lang.scala.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 - */ - def onErrorReturn[U >: T](resumeFunction: Throwable => U): Observable[U] = { - val f1: Func1[Throwable, _ <: U] = resumeFunction - val f2 = f1.asInstanceOf[Func1[Throwable, Nothing]] - toScalaObservable[U](asJavaObservable.onErrorReturn(f2)) - } - - /** - * Intercepts `onError` notifications from the source Observable and replaces them with the - * `onNext` emissions of an Observable returned by a specified function. This allows the source - * sequence to continue even if it issues multiple `onError` notifications. - * - * <img width="640" height="310" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/onErrorFlatMap.png"> - * - * @param resumeFunction a function that accepts an `Throwable` and an `Option` associated with this error representing - * the Throwable issued by the source Observable, and returns an Observable that emits items - * that will be emitted in place of the error. If no value is associated with the error, the value - * will be `None`. - * @return the original Observable, with appropriately modified behavior - */ - def onErrorFlatMap[U >: T](resumeFunction: (Throwable, Option[Any]) => Observable[U]): Observable[U] = { - val f = new Func1[rx.exceptions.OnErrorThrowable, rx.Observable[_ <: U]] { - override def call(t: rx.exceptions.OnErrorThrowable): rx.Observable[_ <: U] = { - val v = if (t.isValueNull) Some(t.getValue) else None - resumeFunction(t.getCause, v).asJavaObservable - } - } - val thisJava = asJavaObservable.asInstanceOf[rx.Observable[U]] - toScalaObservable[U](thisJava.onErrorFlatMap(f)) - } - - /** - * 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. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/reduce.png"> - * - * 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 - * @return an Observable that emits a single item that is the result of accumulating the - * output from the source Observable - */ - def reduce[U >: T](accumulator: (U, U) => U): Observable[U] = { - val func: Func2[_ >: U, _ >: U, _ <: U] = accumulator - val func2 = func.asInstanceOf[Func2[T, T, T]] - toScalaObservable[U](asJavaObservable.asInstanceOf[rx.Observable[T]].reduce(func2)) - } - - /** - * Returns a pair of a start function and an [[rx.lang.scala.Observable]] that shares a single subscription to the underlying - * Observable that will replay all of its items and notifications to any future [[rx.lang.scala.Observer]]. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.png"> - * - * @return a pair of a start function and an [[rx.lang.scala.Observable]] such that when the start function - * is called, the Observable starts to emit items to its [[rx.lang.scala.Observer]]s - */ - def replay: ConnectableObservable[T] = { - new ConnectableObservable[T](asJavaObservable.replay()) - } - - /** - * Returns an Observable that emits items that are the results of invoking a specified selector on the items - * emitted by a `ConnectableObservable` that shares a single subscription to the source Observable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.f.png"> - * - * @param selector the selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Observable - * @return an Observable that emits items that are the results of invoking the selector on a `ConnectableObservable` - * that shares a single subscription to the source Observable - */ - def replay[U >: T, R](selector: Observable[U] => Observable[R]): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - val fJava: Func1[rx.Observable[U], rx.Observable[R]] = - (jo: rx.Observable[U]) => selector(toScalaObservable[U](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable[R](thisJava.replay(fJava)) - } - - /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a `ConnectableObservable` that shares a single subscription to the source Observable, - * replaying `bufferSize` notifications. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.fn.png"> - * - * @param selector the selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Observable - * @param bufferSize the buffer size that limits the number of items the connectable observable can replay - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a `ConnectableObservable` that shares a single subscription to the source Observable replaying - * no more than `bufferSize` items - */ - def replay[U >: T, R](selector: Observable[U] => Observable[R], bufferSize: Int): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - val fJava: Func1[rx.Observable[U], rx.Observable[R]] = - (jo: rx.Observable[U]) => selector(toScalaObservable[U](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable[R](thisJava.replay(fJava, bufferSize)) - } - - /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a `ConnectableObservable` that shares a single subscription to the source Observable, - * replaying no more than `bufferSize` items that were emitted within a specified time window. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.fnt.png"> - * - * @param selector a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Observable - * @param bufferSize the buffer size that limits the number of items the connectable observable can replay - * @param time the duration of the window in which the replayed items must have been emitted - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a `ConnectableObservable` that shares a single subscription to the source Observable, and - * replays no more than `bufferSize` items that were emitted within the window defined by `time` - */ - def replay[U >: T, R](selector: Observable[U] => Observable[R], bufferSize: Int, time: Duration): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - val fJava: Func1[rx.Observable[U], rx.Observable[R]] = - (jo: rx.Observable[U]) => selector(toScalaObservable[U](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable[R](thisJava.replay(fJava, bufferSize, time.length, time.unit)) - } - - /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a `ConnectableObservable` that shares a single subscription to the source Observable, - * replaying no more than `bufferSize` items that were emitted within a specified time window. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.fnts.png"> - * - * @param selector a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Observable - * @param bufferSize the buffer size that limits the number of items the connectable observable can replay - * @param time the duration of the window in which the replayed items must have been emitted - * @param scheduler the Scheduler that is the time source for the window - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a `ConnectableObservable` that shares a single subscription to the source Observable, and - * replays no more than `bufferSize` items that were emitted within the window defined by `time` - * @throws IllegalArgumentException if `bufferSize` is less than zero - */ - def replay[U >: T, R](selector: Observable[U] => Observable[R], bufferSize: Int, time: Duration, scheduler: Scheduler): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - val fJava: Func1[rx.Observable[U], rx.Observable[R]] = - (jo: rx.Observable[U]) => selector(toScalaObservable[U](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable[R](thisJava.replay(fJava, bufferSize, time.length, time.unit, scheduler)) - } - - /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a `ConnectableObservable` that shares a single subscription to the source Observable, - * replaying a maximum of `bufferSize` items. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.fns.png"> - * - * @param selector a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Observable - * @param bufferSize the buffer size that limits the number of items the connectable observable can replay - * @param scheduler the Scheduler on which the replay is observed - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a `ConnectableObservable` that shares a single subscription to the source Observable, - * replaying no more than `bufferSize` notifications - */ - def replay[U >: T, R](selector: Observable[U] => Observable[R], bufferSize: Int, scheduler: Scheduler): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - val fJava: Func1[rx.Observable[U], rx.Observable[R]] = - (jo: rx.Observable[U]) => selector(toScalaObservable[U](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable[R](thisJava.replay(fJava, bufferSize, scheduler)) - } - - /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a `ConnectableObservable` that shares a single subscription to the source Observable, - * replaying all items that were emitted within a specified time window. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.ft.png"> - * - * @param selector a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Observable - * @param time the duration of the window in which the replayed items must have been emitted - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a `ConnectableObservable` that shares a single subscription to the source Observable, - * replaying all items that were emitted within the window defined by `time` - */ - def replay[U >: T, R](selector: Observable[U] => Observable[R], time: Duration, scheduler: Scheduler): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - val fJava: Func1[rx.Observable[U], rx.Observable[R]] = - (jo: rx.Observable[U]) => selector(toScalaObservable[U](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable[R](thisJava.replay(fJava, time.length, time.unit, scheduler)) - } - - /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a `ConnectableObservable` that shares a single subscription to the source Observable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.fs.png"> - * - * @param selector a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Observable - * @param scheduler the Scheduler where the replay is observed - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a `ConnectableObservable` that shares a single subscription to the source Observable, - * replaying all items - */ - def replay[U >: T, R](selector: Observable[U] => Observable[R], scheduler: Scheduler): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - val fJava: Func1[rx.Observable[U], rx.Observable[R]] = - (jo: rx.Observable[U]) => selector(toScalaObservable[U](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable[R](thisJava.replay(fJava, scheduler)) - } - - /** - * Returns a `ConnectableObservable` that shares a single subscription to the source Observable and - * replays at most `bufferSize` items that were emitted during a specified time window. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.nt.png"> - * - * @param bufferSize the buffer size that limits the number of items that can be replayed - * @param time the duration of the window in which the replayed items must have been emitted - * @return a `ConnectableObservable` that shares a single subscription to the source Observable and - * replays at most `bufferSize` items that were emitted during the window defined by `time` - */ - def replay(bufferSize: Int, time: Duration): ConnectableObservable[T] = { - new ConnectableObservable[T](asJavaObservable.replay(bufferSize, time.length, time.unit)) - } - - /** - * Returns a `ConnectableObservable` that shares a single subscription to the source Observable and - * that replays a maximum of `bufferSize` items that are emitted within a specified time window. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.nts.png"> - * - * @param bufferSize the buffer size that limits the number of items that can be replayed - * @param time the duration of the window in which the replayed items must have been emitted - * @param scheduler the scheduler that is used as a time source for the window - * @return a `ConnectableObservable` that shares a single subscription to the source Observable and - * replays at most `bufferSize` items that were emitted during the window defined by `time` - *@throws IllegalArgumentException if `bufferSize` is less than zero - */ - def replay(bufferSize: Int, time: Duration, scheduler: Scheduler): ConnectableObservable[T] = { - new ConnectableObservable[T](asJavaObservable.replay(bufferSize, time.length, time.unit, scheduler)) - } - - /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a `ConnectableObservable` that shares a single subscription to the source Observable, - * replaying all items that were emitted within a specified time window. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.ft.png"> - * - * @param selector a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Observable - * @param time the duration of the window in which the replayed items must have been emitted - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a `ConnectableObservable` that shares a single subscription to the source Observable, - * replaying all items that were emitted within the window defined by `time` - */ - def replay[U >: T, R](selector: Observable[U] => Observable[R], time: Duration): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - val fJava: Func1[rx.Observable[U], rx.Observable[R]] = - (jo: rx.Observable[U]) => selector(toScalaObservable[U](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable[R](thisJava.replay(fJava, time.length, time.unit)) - } - - /** - * Returns a `ConnectableObservable` that shares a single subscription to the source Observable that - * replays at most `bufferSize` items emitted by that Observable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.n.png"> - * - * @param bufferSize the buffer size that limits the number of items that can be replayed - * @return a `ConnectableObservable` that shares a single subscription to the source Observable and - * replays at most `bufferSize` items emitted by that Observable - */ - def replay(bufferSize: Int): ConnectableObservable[T] = { - new ConnectableObservable[T](asJavaObservable.replay(bufferSize)) - } - - /** - * Returns a `ConnectableObservable` that shares a single subscription to the source Observable and - * replays at most `bufferSize` items emitted by that Observable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.ns.png"> - * - * @param bufferSize the buffer size that limits the number of items that can be replayed - * @param scheduler the scheduler on which the Observers will observe the emitted items - * @return a `ConnectableObservable` that shares a single subscription to the source Observable and - * replays at most `bufferSize` items that were emitted by the Observable - */ - def replay(bufferSize: Int, scheduler: Scheduler): ConnectableObservable[T] = { - new ConnectableObservable[T](asJavaObservable.replay(bufferSize, scheduler)) - } - - /** - * Returns a `ConnectableObservable` that shares a single subscription to the source Observable and - * replays all items emitted by that Observable within a specified time window. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.t.png"> - * - * @param time the duration of the window in which the replayed items must have been emitted - * @return a `ConnectableObservable` that shares a single subscription to the source Observable and - * replays the items that were emitted during the window defined by `time` - */ - def replay(time: Duration): ConnectableObservable[T] = { - new ConnectableObservable[T](asJavaObservable.replay(time.length, time.unit)) - } - - /** - * Returns a `ConnectableObservable` that shares a single subscription to the source Observable and - * replays all items emitted by that Observable within a specified time window. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.ts.png"> - * - * @param time the duration of the window in which the replayed items must have been emitted - * @param scheduler the Scheduler that is the time source for the window - * @return a `ConnectableObservable` that shares a single subscription to the source Observable and - * replays the items that were emitted during the window defined by `time` - */ - def replay(time: Duration, scheduler: Scheduler): ConnectableObservable[T] = { - new ConnectableObservable[T](asJavaObservable.replay(time.length, time.unit, scheduler)) - } - - /** - * Returns a `ConnectableObservable` that shares a single subscription to the source Observable that - * will replay all of its items and notifications to any future `Observer` on the given `Scheduler`. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/replay.s.png"> - * - * @param scheduler the Scheduler on which the Observers will observe the emitted items - * @return a `ConnectableObservable` that shares a single subscription to the source Observable that - * will replay all of its items and notifications to any future `bserver` on the given `Scheduler` - */ - def replay(scheduler: Scheduler): ConnectableObservable[T] = { - new ConnectableObservable[T](asJavaObservable.replay(scheduler)) - } - - /** - * This method has similar behavior to [[rx.lang.scala.Observable.replay]] except that this auto-subscribes to - * the source Observable rather than returning a start function and an Observable. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/cache.png"> - * - * This is useful when you want an Observable to cache responses and you can't control the - * subscribe/unsubscribe behavior of all the [[rx.lang.scala.Observer]]s. - * - * When you call `cache`, it does not yet subscribe to the - * source Observable. This only happens when `subscribe` is called - * the first time on the Observable returned by `cache()`. - * - * 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. - */ - def cache: Observable[T] = { - toScalaObservable[T](asJavaObservable.cache()) - } - - /** - * Returns a new [[Observable]] that multicasts (shares) the original [[Observable]]. As long a - * there is more than 1 [[Subscriber]], this [[Observable]] will be subscribed and emitting data. - * When all subscribers have unsubscribed it will unsubscribe from the source [[Observable]]. - * - * This is an alias for `publish().refCount()` - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/publishRefCount.png"> - * - * @return a [[Observable]] that upon connection causes the source Observable to emit items to its [[Subscriber]]s - * @since 0.19 - */ - def share: Observable[T] = { - toScalaObservable[T](asJavaObservable.share()) - } - - /** - * Returns an Observable that emits a Boolean that indicates whether the source Observable emitted a - * specified item. - * - * Note: this method uses `==` to compare elements. It's a bit different from RxJava which uses `Object.equals`. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/contains.png"> - * - *@param elem the item to search for in the emissions from the source Observable - * @return an Observable that emits `true` if the specified item is emitted by the source Observable, - * or `false` if the source Observable completes without emitting that item - */ - def contains[U >: T](elem: U): Observable[Boolean] = { - exists(_ == elem) - } - - /** - * Returns a a pair of a start function and an [[rx.lang.scala.Observable]], which waits until the start function is called before it begins emitting - * items to those [[rx.lang.scala.Observer]]s that have subscribed to it. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/publishConnect.png"> - * - * @return an [[rx.lang.scala.observables.ConnectableObservable]]. - */ - def publish: ConnectableObservable[T] = { - new ConnectableObservable[T](asJavaObservable.publish()) - } - - - /** - * Returns an Observable that emits `initialValue` followed by the items emitted by a `ConnectableObservable` that shares a single subscription to the source Observable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/publishConnect.i.png"> - * - * @param initialValue the initial value to be emitted by the resulting Observable - * @return a `ConnectableObservable` that shares a single subscription to the underlying Observable and starts with `initialValue` - */ - def publish[U >: T](initialValue: U): ConnectableObservable[U] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - new ConnectableObservable[U](thisJava.publish(initialValue)) - } - - /** - * Returns an Observable that emits the results of invoking a specified selector on items emitted by a `ConnectableObservable` - * that shares a single subscription to the underlying sequence. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/publishConnect.f.png"> - * - * @param selector a function that can use the multicasted source sequence as many times as needed, without - * causing multiple subscriptions to the source sequence. Subscribers to the given source will - * receive all notifications of the source from the time of the subscription forward. - * @return an Observable that emits the results of invoking the selector on the items emitted by a `ConnectableObservable` - * that shares a single subscription to the underlying sequence - */ - def publish[U >: T, R](selector: Observable[U] => Observable[R]): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - val fJava: Func1[rx.Observable[U], rx.Observable[R]] = - (jo: rx.Observable[U]) => selector(toScalaObservable[U](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable[R](thisJava.publish(fJava)) - } - - /** - * Returns an Observable that emits `initialValue` followed by the results of invoking a specified - * selector on items emitted by a `ConnectableObservable` that shares a single subscription to the - * source Observable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/publishConnect.if.png"> - * - * @param selector a function that can use the multicasted source sequence as many times as needed, without - * causing multiple subscriptions to the source Observable. Subscribers to the source will - * receive all notifications of the source from the time of the subscription forward - * @param initialValue the initial value of the underlying `BehaviorSubject` - * @return an Observable that emits `initialValue` followed by the results of invoking the selector - * on a `ConnectableObservable` that shares a single subscription to the underlying Observable - */ - def publish[U >: T, R](selector: Observable[U] => Observable[R], initialValue: U): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - val fJava: Func1[rx.Observable[U], rx.Observable[R]] = - (jo: rx.Observable[U]) => selector(toScalaObservable[U](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable[R](thisJava.publish(fJava, initialValue)) - } - - /** - * Returns a [[ConnectableObservable]] that emits only the last item emitted by the source Observable. - * A [[ConnectableObservable]] resembles an ordinary Observable, except that it does not begin emitting items - * when it is subscribed to, but only when its `connect` method is called. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/publishLast.png"> - * - * @return a [[ConnectableObservable]] that emits only the last item emitted by the source Observable - */ - def publishLast: ConnectableObservable[T] = { - new ConnectableObservable[T](asJavaObservable.publishLast()) - } - - /** - * Returns an Observable that emits an item that results from invoking a specified selector on the last item - * emitted by a [[ConnectableObservable]] that shares a single subscription to the source Observable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/publishLast.f.png"> - * - * @param selector a function that can use the multicasted source sequence as many times as needed, without - * causing multiple subscriptions to the source Observable. Subscribers to the source will only - * receive the last item emitted by the source. - * @return an Observable that emits an item that is the result of invoking the selector on a [[ConnectableObservable]] - * that shares a single subscription to the source Observable - */ - def publishLast[R](selector: Observable[T] => Observable[R]): Observable[R] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[T]] - val fJava = new rx.functions.Func1[rx.Observable[T], rx.Observable[R]]() { - override def call(jo: rx.Observable[T]): rx.Observable[R] = - selector(toScalaObservable[T](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - } - toScalaObservable[R](thisJava.publishLast(fJava)) - } - - // TODO add Scala-like aggregate function - - /** - * 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 an 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. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/reduceSeed.png"> - * - * 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 - * 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 accumulating the output - * from the items emitted by the source Observable - */ - def foldLeft[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { - toScalaObservable[R](asJavaObservable.reduce(initialValue, new Func2[R,T,R]{ - def call(t1: R, t2: T): R = accumulator(t1,t2) - })) - } - - /** - * Returns an Observable that emits the results of sampling the items emitted by the source - * Observable at a specified time interval. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/sample.png"> - * - * @param duration the sampling rate - * @return an Observable that emits the results of sampling the items emitted by the source - * Observable at the specified time interval - */ - def sample(duration: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.sample(duration.length, duration.unit)) - } - - /** - * Returns an Observable that emits the results of sampling the items emitted by the source - * Observable at a specified time interval. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/sample.png"> - * - * @param duration the sampling rate - * @param scheduler - * the [[rx.lang.scala.Scheduler]] to use when sampling - * @return an Observable that emits the results of sampling the items emitted by the source - * Observable at the specified time interval - */ - def sample(duration: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.sample(duration.length, duration.unit, scheduler)) - } - - /** - * Return an Observable that emits the results of sampling the items emitted by the source Observable - * whenever the specified sampler Observable emits an item or completes. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/sample.o.png"> - * - * @param sampler - * the Observable to use for sampling the source Observable - * @return an Observable that emits the results of sampling the items emitted by this Observable whenever - * the sampler Observable emits an item or completes - */ - def sample(sampler: Observable[Any]): Observable[T] = { - toScalaObservable[T](asJavaObservable.sample(sampler)) - } - - /** - * 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 an 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. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/scanSeed.png"> - * - * This sort of function is sometimes called an 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 [[rx.lang.scala.Observer]]s via - * [[rx.lang.scala.Observer.onNext onNext]] and used in the next accumulator call. - * @return an Observable that emits the results of each call to the accumulator function - */ - def scan[R](initialValue: R)(accumulator: (R, T) => R): Observable[R] = { - toScalaObservable[R](asJavaObservable.scan(initialValue, new Func2[R,T,R]{ - def call(t1: R, t2: T): R = accumulator(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 an 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. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/scan.png"> - * <p> - * - * @param accumulator - * an accumulator function to be invoked on each item emitted by the source - * Observable, whose result will be emitted to [[rx.lang.scala.Observer]]s via - * [[rx.lang.scala.Observer.onNext onNext]] and used in the next accumulator call. - * @return - * an Observable that emits the results of each call to the - * accumulator function - */ - def scan[U >: T](accumulator: (U, U) => U): Observable[U] = { - val func: Func2[_ >: U, _ >: U, _ <: U] = accumulator - val func2 = func.asInstanceOf[Func2[T, T, T]] - toScalaObservable[U](asJavaObservable.asInstanceOf[rx.Observable[T]].scan(func2)) - } - - /** - * Returns an Observable that emits a Boolean that indicates whether all of the items emitted by - * the source Observable satisfy a condition. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/all.png"> - * - * @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` - */ - def forall(predicate: T => Boolean): Observable[Boolean] = { - // type mismatch; found : rx.Observable[java.lang.Boolean] required: rx.Observable[_ <: scala.Boolean] - // new Observable[Boolean](asJavaNotification.all(predicate)) - // it's more fun in Scala: - this.map(predicate).foldLeft(true)(_ && _) - } - - /** - * Returns an Observable that skips the first `num` items emitted by the source - * Observable and emits the remainder. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/skip.png"> - * - * @param n - * 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 - */ - def drop(n: Int): Observable[T] = { - toScalaObservable[T](asJavaObservable.skip(n)) - } - - /** - * Returns an Observable that drops values emitted by the source Observable before a specified time window - * elapses. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/skip.t.png"> - * - * @param time the length of the time window to drop - * @return an Observable that drops values emitted by the source Observable before the time window defined - * by `time` elapses and emits the remainder - */ - def drop(time: Duration): Observable[T] = { - toScalaObservable(asJavaObservable.skip(time.length, time.unit)) - } - - /** - * Returns an Observable that drops values emitted by the source Observable before a specified time window - * elapses. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/skip.t.png"> - * - * @param time the length of the time window to drop - * @param scheduler the `Scheduler` on which the timed wait happens - * @return an Observable that drops values emitted by the source Observable before the time window defined - * by `time` elapses and emits the remainder - */ - def drop(time: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable(asJavaObservable.skip(time.length, time.unit, scheduler)) - } - - /** - * Returns an Observable that bypasses all items from the source Observable as long as the specified - * condition holds true. Emits all further source items as soon as the condition becomes false. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/skipWhile.png"> - * - * @param predicate - * A function to test each item emitted from the source Observable for a condition. - * @return an Observable that emits all items from the source Observable as soon as the condition - * becomes false. - */ - def dropWhile(predicate: T => Boolean): Observable[T] = { - toScalaObservable(asJavaObservable.skipWhile(predicate)) - } - - /** - * Returns an Observable that drops a specified number of items from the end of the sequence emitted by the - * source Observable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/skipLast.png"> - * <p> - * This Observer accumulates a queue long enough to store the first `n` items. As more items are - * received, items are taken from the front of the queue and emitted by the returned Observable. This causes - * such items to be delayed. - * - * @param n number of items to drop from the end of the source sequence - * @return an Observable that emits the items emitted by the source Observable except for the dropped ones - * at the end - * @throws IndexOutOfBoundsException if `n` is less than zero - */ - def dropRight(n: Int): Observable[T] = { - toScalaObservable(asJavaObservable.skipLast(n)) - } - - /** - * Returns an Observable that drops items emitted by the source Observable during a specified time window - * before the source completes. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/skipLast.t.png"> - * - * Note: this action will cache the latest items arriving in the specified time window. - * - * @param time the length of the time window - * @return an Observable that drops those items emitted by the source Observable in a time window before the - * source completes defined by `time` - */ - def dropRight(time: Duration): Observable[T] = { - toScalaObservable(asJavaObservable.skipLast(time.length, time.unit)) - } - - /** - * Returns an Observable that drops items emitted by the source Observable during a specified time window - * (defined on a specified scheduler) before the source completes. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/skipLast.ts.png"> - * - * Note: this action will cache the latest items arriving in the specified time window. - * - * @param time the length of the time window - * @param scheduler the scheduler used as the time source - * @return an Observable that drops those items emitted by the source Observable in a time window before the - * source completes defined by `time` and `scheduler` - */ - def dropRight(time: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable(asJavaObservable.skipLast(time.length, time.unit, scheduler)) - } - - /** - * Returns an Observable that skips items emitted by the source Observable until a second Observable emits an item. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/skipUntil.png"> - * - * @param other the second Observable that has to emit an item before the source Observable's elements begin - * to be mirrored by the resulting Observable - * @return an Observable that skips items from the source Observable until the second Observable emits an - * item, then emits the remaining items - * @see <a href="https://github.com/Netflix/RxJava/wiki/Filtering-Observables#wiki-skipuntil">RxJava Wiki: skipUntil()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229358.aspx">MSDN: Observable.SkipUntil</a> - */ - def dropUntil[E](other: Observable[E]): Observable[T] = { - toScalaObservable[T](asJavaObservable.skipUntil(other)) - } - - /** - * Returns an Observable that emits only the first `num` items emitted by the source - * Observable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/take.png"> - * - * This method returns an Observable that will invoke a subscribing [[rx.lang.scala.Observer]]'s - * [[rx.lang.scala.Observer.onNext onNext]] function a maximum of `num` times before invoking - * [[rx.lang.scala.Observer.onCompleted onCompleted]]. - * - * @param n - * 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 - */ - def take(n: Int): Observable[T] = { - toScalaObservable[T](asJavaObservable.take(n)) - } - - /** - * Returns an Observable that emits those items emitted by source Observable before a specified time runs out. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/take.t.png"> - * - * @param time the length of the time window - * @return an Observable that emits those items emitted by the source Observable before the time runs out - */ - def take(time: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.take(time.length, time.unit)) - } - - /** - * Returns an Observable that emits those items emitted by source Observable before a specified time (on - * specified Scheduler) runs out - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/take.ts.png"> - * - * @param time the length of the time window - * @param scheduler the Scheduler used for time source - * @return an Observable that emits those items emitted by the source Observable before the time runs out, - * according to the specified Scheduler - */ - def take(time: Duration, scheduler: Scheduler) { - toScalaObservable[T](asJavaObservable.take(time.length, time.unit, scheduler.asJavaScheduler)) - } - - /** - * Returns an Observable that emits items emitted by the source Observable so long as a - * specified condition is true. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/takeWhile.png"> - * - * @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` - */ - def takeWhile(predicate: T => Boolean): Observable[T] = { - toScalaObservable[T](asJavaObservable.takeWhile(predicate)) - } - - /** - * Returns an Observable that emits only the last `count` items emitted by the source - * Observable. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/last.png"> - * - * @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 - */ - def takeRight(count: Int): Observable[T] = { - toScalaObservable[T](asJavaObservable.takeLast(count)) - } - - /** - * Return an Observable that emits the items from the source Observable that were emitted in a specified - * window of `time` before the Observable completed. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/takeLast.t.png"> - * - * @param time the length of the time window - * @return an Observable that emits the items from the source Observable that were emitted in the window of - * time before the Observable completed specified by `time` - */ - def takeRight(time: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.takeLast(time.length, time.unit)) - } - - /** - * Return an Observable that emits the items from the source Observable that were emitted in a specified - * window of `time` before the Observable completed, where the timing information is provided by a specified - * Scheduler. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/takeLast.ts.png"> - * - * @param time the length of the time window - * @param scheduler the Scheduler that provides the timestamps for the Observed items - * @return an Observable that emits the items from the source Observable that were emitted in the window of - * time before the Observable completed specified by `time`, where the timing information is - * provided by `scheduler` - */ - def takeRight(time: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.takeLast(time.length, time.unit, scheduler.asJavaScheduler)) - } - - /** - * Return an Observable that emits at most a specified number of items from the source Observable that were - * emitted in a specified window of time before the Observable completed. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/takeLast.tn.png"> - * - * @param count the maximum number of items to emit - * @param time the length of the time window - * @return an Observable that emits at most `count` items from the source Observable that were emitted - * in a specified window of time before the Observable completed - * @throws IllegalArgumentException if `count` is less than zero - */ - def takeRight(count: Int, time: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.takeLast(count, time.length, time.unit)) - } - - /** - * Return an Observable that emits at most a specified number of items from the source Observable that were - * emitted in a specified window of `time` before the Observable completed, where the timing information is - * provided by a given Scheduler. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/takeLast.tns.png"> - * - * @param count the maximum number of items to emit - * @param time the length of the time window - * @param scheduler the Scheduler that provides the timestamps for the observed items - * @return an Observable that emits at most `count` items from the source Observable that were emitted - * in a specified window of time before the Observable completed, where the timing information is - * provided by the given `scheduler` - * @throws IllegalArgumentException if `count` is less than zero - */ - def takeRight(count: Int, time: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.takeLast(count, time.length, time.unit, scheduler.asJavaScheduler)) - } - - /** - * Returns an Observable that emits the items from the source Observable only until the - * `other` Observable emits an item. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/takeUntil.png"> - * - * @param that - * the Observable whose first emitted item will cause `takeUntil` to stop - * emitting items from the source Observable - * @tparam E - * the type of items emitted by `other` - * @return an Observable that emits the items of the source Observable until such time as - * `other` emits its first item - */ - def takeUntil[E](that: Observable[E]): Observable[T] = { - toScalaObservable[T](asJavaObservable.takeUntil(that.asJavaObservable)) - } - - /** - * Returns an Observable that emits a single item, a list composed of all the items emitted by - * the source Observable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/toList.png"> - * - * Normally, an Observable that returns multiple items will do so by invoking its [[rx.lang.scala.Observer]]'s - * [[rx.lang.scala.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 `Observable.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. - */ - def toSeq: Observable[Seq[T]] = { - Observable.jObsOfListToScObsOfSeq(asJavaObservable.toList) - : Observable[Seq[T]] // SI-7818 - } - - /** - * Groups the items emitted by this Observable according to a specified discriminator function. - * - * @param f - * a function that extracts the key from an item - * @tparam K - * the type of keys returned by the discriminator function. - * @return an Observable that emits `(key, observable)` pairs, where `observable` - * contains all items for which `f` returned `key`. - */ - def groupBy[K](f: T => K): Observable[(K, Observable[T])] = { - val o1 = asJavaObservable.groupBy[K](f) : rx.Observable[_ <: rx.observables.GroupedObservable[K, _ <: T]] - val func = (o: rx.observables.GroupedObservable[K, _ <: T]) => (o.getKey, toScalaObservable[T](o)) - toScalaObservable[(K, Observable[T])](o1.map[(K, Observable[T])](func)) - } - - /** - * Groups the items emitted by this Observable according to a specified discriminator function and terminates these groups - * according to a function. - * - * @param f - * a function that extracts the key from an item - * @param closings - * the function that accepts the key of a given group and an observable representing that group, and returns - * an observable that emits a single Closing when the group should be closed. - * @tparam K - * the type of the keys returned by the discriminator function. - * @return an Observable that emits `(key, observable)` pairs, where `observable` - * contains all items for which `f` returned `key` before `closings` emits a value. - */ - def groupByUntil[K](f: T => K)(closings: (K, Observable[T])=>Observable[Any]): Observable[(K, Observable[T])] = { - val fclosing: Func1[_ >: rx.observables.GroupedObservable[K, _ <: T], _ <: rx.Observable[_ <: Any]] = - (jGrObs: rx.observables.GroupedObservable[K, _ <: T]) => closings(jGrObs.getKey, toScalaObservable[T](jGrObs)).asJavaObservable - val o1 = asJavaObservable.groupByUntil[K, Any](f, fclosing) : rx.Observable[_ <: rx.observables.GroupedObservable[K, _ <: T]] - val func = (o: rx.observables.GroupedObservable[K, _ <: T]) => (o.getKey, toScalaObservable[T](o)) - toScalaObservable[(K, Observable[T])](o1.map[(K, Observable[T])](func)) - } - - /** - * Groups the items emitted by an [[Observable]] (transformed by a selector) according to a specified key selector function - * until the duration Observable expires for the key. - * - * <img width="640" height="375" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/groupByUntil.png"> - * - * <em>Note:</em> The `Observable` in the pair `(K, Observable[V])` will cache the items it is to emit until such time as it - * is subscribed to. For this reason, in order to avoid memory leaks, you should not simply ignore those `Observable` that - * do not concern you. Instead, you can signal to them that they may discard their buffers by applying an operator like `take(0)` to them. - * - * @param keySelector a function to extract the key for each item - * @param valueSelector a function to map each item emitted by the source [[Observable]] to an item emitted by one - * of the resulting `Observable[V]`s - * @param closings a function to signal the expiration of a group - * @return an [[Observable]] that emits pairs of key and `Observable[V]`, each of which corresponds to a key - * value and each of which emits all items emitted by the source [[Observable]] during that - * key's duration that share that same key value, transformed by the value selector - */ - def groupByUntil[K, V](keySelector: T => K, valueSelector: T => V)(closings: (K, Observable[V]) => Observable[Any]): Observable[(K, Observable[V])] = { - val jKeySelector: Func1[_ >: T, _ <: K] = keySelector - val jValueSelector: Func1[_ >: T, _ <: V] = valueSelector - val jDurationSelector = new Func1[rx.observables.GroupedObservable[_ <: K, _ <: V], rx.Observable[_ <: Any]] { - override def call(jgo: rx.observables.GroupedObservable[_ <: K, _ <: V]): rx.Observable[_ <: Any] = closings(jgo.getKey, toScalaObservable[V](jgo)) - } - val f = (o: rx.observables.GroupedObservable[K, _ <: V]) => (o.getKey, toScalaObservable[V](o)) - val jo = asJavaObservable.groupByUntil[K, V, Any](jKeySelector, jValueSelector, jDurationSelector).map[(K, Observable[V])](f) - toScalaObservable[(K, Observable[V])](jo) - } - - /** - * Correlates the items emitted by two Observables based on overlapping durations. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/join_.png"> - * - * @param other - * the second Observable to join items from - * @param leftDurationSelector - * a function to select a duration for each item emitted by the source Observable, - * used to determine overlap - * @param rightDurationSelector - * a function to select a duration for each item emitted by the inner Observable, - * used to determine overlap - * @param resultSelector - * a function that computes an item to be emitted by the resulting Observable for any - * two overlapping items emitted by the two Observables - * @return - * an Observable that emits items correlating to items emitted by the source Observables - * that have overlapping durations - * @see <a href="https://github.com/Netflix/RxJava/wiki/Combining-Observables#join">RxJava Wiki: join()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229750.aspx">MSDN: Observable.Join</a> - */ - def join[S, R] (other: Observable[S])(leftDurationSelector: T => Observable[Any], rightDurationSelector: S => Observable[Any], resultSelector: (T, S) => R): Observable[R] = { - val outer : rx.Observable[_ <: T] = this.asJavaObservable - val inner : rx.Observable[_ <: S] = other.asJavaObservable - val left: Func1[_ >: T, _<: rx.Observable[_ <: Any]] = (t: T) => leftDurationSelector(t).asJavaObservable - val right: Func1[_ >: S, _<: rx.Observable[_ <: Any]] = (s: S) => rightDurationSelector(s).asJavaObservable - val f: Func2[_>: T, _ >: S, _ <: R] = resultSelector - - toScalaObservable[R]( - outer.asInstanceOf[rx.Observable[T]].join[S, Any, Any, R]( - inner.asInstanceOf[rx.Observable[S]], - left. asInstanceOf[Func1[T, rx.Observable[Any]]], - right.asInstanceOf[Func1[S, rx.Observable[Any]]], - f.asInstanceOf[Func2[T,S,R]]) - ) - } - - /** - * Returns an Observable that correlates two Observables when they overlap in time and groups the results. - * - * <img width="640" height="380" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/groupJoin.png"> - * - * @param other the other Observable to correlate items from the source Observable with - * @param leftDuration a function that returns an Observable whose emissions indicate the duration of the values of - * the source Observable - * @param rightDuration a function that returns an Observable whose emissions indicate the duration of the values of - * the `other` Observable - * @param resultSelector a function that takes an item emitted by each Observable and returns the value to be emitted - * by the resulting Observable - * @return an Observable that emits items based on combining those items emitted by the source Observables - * whose durations overlap - */ - def groupJoin[S, R](other: Observable[S])(leftDuration: T => Observable[Any], rightDuration: S => Observable[Any], resultSelector: (T, Observable[S]) => R): Observable[R] = { - val outer: rx.Observable[_ <: T] = this.asJavaObservable - val inner: rx.Observable[_ <: S] = other.asJavaObservable - val left: Func1[_ >: T, _ <: rx.Observable[_ <: Any]] = (t: T) => leftDuration(t).asJavaObservable - val right: Func1[_ >: S, _ <: rx.Observable[_ <: Any]] = (s: S) => rightDuration(s).asJavaObservable - val f: Func2[_ >: T, _ >: rx.Observable[S], _ <: R] = (t: T, o: rx.Observable[S]) => resultSelector(t, toScalaObservable[S](o)) - toScalaObservable[R]( - outer.asInstanceOf[rx.Observable[T]].groupJoin[S, Any, Any, R]( - inner.asInstanceOf[rx.Observable[S]], - left.asInstanceOf[Func1[T, rx.Observable[Any]]], - right.asInstanceOf[Func1[S, rx.Observable[Any]]], - f) - ) - } - - /** - * Returns a new Observable by applying a function that you supply to each item emitted by the source - * Observable that returns an Observable, and then emitting the items emitted by the most recently emitted - * of these Observables. - * - * <img width="640" height="350" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/switchMap.png"> - * - * @param f a function that, when applied to an item emitted by the source Observable, returns an Observable - * @return an Observable that emits the items emitted by the Observable returned from applying a function to - * the most recently emitted item emitted by the source Observable - */ - def switchMap[R](f: T => Observable[R]): Observable[R] = { - toScalaObservable[R](asJavaObservable.switchMap[R](new Func1[T, rx.Observable[_ <: R]] { - def call(t: T): rx.Observable[_ <: R] = f(t).asJavaObservable - })) - } - - /** - * Given an Observable that emits Observables, creates a single Observable that - * emits the items emitted by the most recently published of those Observables. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/switchDo.png"> - * - * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, - * otherwise you'll get a compilation error. - * - * @return an Observable that emits only the items emitted by the most recently published - * Observable - * - * @usecase def switch[U]: Observable[U] - * @inheritdoc - */ - def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { - val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable - val o5 = rx.Observable.switchOnNext[U](o4) - toScalaObservable[U](o5) - } - // Naming: We follow C# (switch), not Java (switchOnNext), because Java just had to avoid clash with keyword - - /** - * Flattens two Observables into one Observable, without any transformation. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/merge.png"> - * - * You can combine items emitted by two Observables so that they act like a single - * Observable by using the `merge` method. - * - * @param that - * an Observable to be merged - * @return an Observable that emits items from `this` and `that` until - * `this` or `that` emits `onError` or `onComplete`. - */ - def merge[U >: T](that: Observable[U]): Observable[U] = { - val thisJava: rx.Observable[_ <: U] = this.asJavaObservable - val thatJava: rx.Observable[_ <: U] = that.asJavaObservable - toScalaObservable[U](rx.Observable.merge(thisJava, thatJava)) - } - - /** - * This behaves like [[rx.lang.scala.Observable.merge]] except that if any of the merged Observables - * notify of an error via [[rx.lang.scala.Observer.onError onError]], `mergeDelayError` will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/mergeDelayError.png"> - * - * Even if multiple merged Observables send `onError` notifications, `mergeDelayError` will only invoke the `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 that - * an Observable to be merged - * @return an Observable that emits items that are the result of flattening the items emitted by - * `this` and `that` - */ - def mergeDelayError[U >: T](that: Observable[U]): Observable[U] = { - toScalaObservable[U](rx.Observable.mergeDelayError[U](this.asJavaObservable, that.asJavaObservable)) - } - - /** - * Flattens the sequence of Observables emitted by `this` into one Observable, without any - * transformation. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/merge.png"> - * - * You can combine the items emitted by multiple Observables so that they act like a single - * Observable by using this method. - * - * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, - * otherwise you'll get a compilation error. - * - * @return an Observable that emits items that are the result of flattening the items emitted - * by the Observables emitted by `this` - * - * @usecase def flatten[U]: Observable[U] - * @inheritdoc - */ - def flatten[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { - val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable - val o5 = rx.Observable.merge[U](o4) - toScalaObservable[U](o5) - } - - /** - * Flattens an Observable that emits Observables into a single Observable that emits the items emitted by - * those Observables, without any transformation, while limiting the maximum number of concurrent - * subscriptions to these Observables. - * - * <img width="640" height="370" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/merge.oo.png"> - * - * You can combine the items emitted by multiple Observables so that they appear as a single Observable, by - * using the `flatten` method. - * - * @param maxConcurrent the maximum number of Observables that may be subscribed to concurrently - * @return an Observable that emits items that are the result of flattening the Observables emitted by the `source` Observable - * @throws IllegalArgumentException if `maxConcurrent` is less than or equal to 0 - */ - def flatten[U](maxConcurrent: Int)(implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { - val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable - val o5 = rx.Observable.merge[U](o4, maxConcurrent) - toScalaObservable[U](o5) - } - - /** - * This behaves like `flatten` except that if any of the merged Observables - * notify of an error via [[rx.lang.scala.Observer.onError onError]], this method will - * refrain from propagating that error notification until all of the merged Observables have - * finished emitting items. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/mergeDelayError.png"> - * - * Even if multiple merged Observables send `onError` notifications, this method will only invoke the `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. - * - * This operation is only available if `this` is of type `Observable[Observable[U]]` for some `U`, - * otherwise you'll get a compilation error. - * - * @return an Observable that emits items that are the result of flattening the items emitted by - * the Observables emitted by the this Observable - * - * @usecase def flattenDelayError[U]: Observable[U] - * @inheritdoc - */ - def flattenDelayError[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] = { - val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[_ <: U]] = o2.map(_.asJavaObservable) - val o4: rx.Observable[_ <: rx.Observable[_ <: U]] = o3.asJavaObservable - val o5 = rx.Observable.mergeDelayError[U](o4) - toScalaObservable[U](o5) - } - - /** - * Combines two observables, emitting a pair 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 that - * The second source observable. - * @return An Observable that combines the source Observables - */ - def combineLatest[U](that: Observable[U]): Observable[(T, U)] = { - val f: Func2[_ >: T, _ >: U, _ <: (T, U)] = (t: T, u: U) => (t, u) - toScalaObservable[(T, U)](rx.Observable.combineLatest[T, U, (T, U)](this.asJavaObservable, that.asJavaObservable, f)) - } - - - /** - * Combines two observables, emitting some type `R` specified in the function `selector`, - * each time an event is received from one of the source observables, where the aggregation - * is defined by the given function. - * - * @param that The second source observable. - * @param selector The function that is used combine the emissions of the two observables. - * @return An Observable that combines the source Observables according to the function `selector`. - */ - def combineLatestWith[U, R](that: Observable[U])(selector: (T, U) => R): Observable[R] = { - toScalaObservable[R](rx.Observable.combineLatest[T, U, R](this.asJavaObservable, that.asJavaObservable, selector)) - } - - /** - * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. - * - * NOTE: If events keep firing faster than the timeout then no data will be emitted. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/throttleWithTimeout.png"> - * - * $debounceVsThrottle - * - * @param timeout - * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. - * - * @return An [[rx.lang.scala.Observable]] which filters out values which are too quickly followed up with newer values. - * @see `Observable.debounce` - */ - def throttleWithTimeout(timeout: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.throttleWithTimeout(timeout.length, timeout.unit)) - } - - /** - * Return an Observable that mirrors the source Observable, except that it drops items emitted by the source - * Observable that are followed by another item within a computed debounce duration. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/debounce.f.png"> - * - * @param debounceSelector function to retrieve a sequence that indicates the throttle duration for each item - * @return an Observable that omits items emitted by the source Observable that are followed by another item - * within a computed debounce duration - */ - def debounce(debounceSelector: T => Observable[Any]): Observable[T] = { - val fJava = new rx.functions.Func1[T, rx.Observable[Any]] { - override def call(t: T) = debounceSelector(t).asJavaObservable.asInstanceOf[rx.Observable[Any]] - } - toScalaObservable[T](asJavaObservable.debounce[Any](fJava)) - } - - /** - * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. - * - * NOTE: If events keep firing faster than the timeout then no data will be emitted. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/debounce.png"> - * - * $debounceVsThrottle - * - * @param timeout - * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. - * - * @return An [[rx.lang.scala.Observable]] which filters out values which are too quickly followed up with newer values. - * @see `Observable.throttleWithTimeout` - */ - def debounce(timeout: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.debounce(timeout.length, timeout.unit)) - } - - /** - * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. - * - * NOTE: If events keep firing faster than the timeout then no data will be emitted. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/debounce.png"> - * - * $debounceVsThrottle - * - * @param timeout - * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. - * @param scheduler - * The [[rx.lang.scala.Scheduler]] to use internally to manage the timers which handle timeout for each event. - * @return Observable which performs the throttle operation. - * @see `Observable.throttleWithTimeout` - */ - def debounce(timeout: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.debounce(timeout.length, timeout.unit, scheduler)) - } - - /** - * Debounces by dropping all values that are followed by newer values before the timeout value expires. The timer resets on each `onNext` call. - * - * NOTE: If events keep firing faster than the timeout then no data will be emitted. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/throttleWithTimeout.png"> - * - * @param timeout - * The time each value has to be 'the most recent' of the [[rx.lang.scala.Observable]] to ensure that it's not dropped. - * @param scheduler - * The [[rx.lang.scala.Scheduler]] to use internally to manage the timers which handle timeout for each event. - * @return Observable which performs the throttle operation. - * @see `Observable.debounce` - */ - def throttleWithTimeout(timeout: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.throttleWithTimeout(timeout.length, timeout.unit, scheduler)) - } - - /** - * Throttles by skipping value until `skipDuration` passes and then emits the next received value. - * - * This differs from `Observable.throttleLast` in that this only tracks passage of time whereas `Observable.throttleLast` ticks at scheduled intervals. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/throttleFirst.png"> - * - * @param skipDuration - * Time to wait before sending another value after emitting last value. - * @param scheduler - * The [[rx.lang.scala.Scheduler]] to use internally to manage the timers which handle timeout for each event. - * @return Observable which performs the throttle operation. - */ - def throttleFirst(skipDuration: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.throttleFirst(skipDuration.length, skipDuration.unit, scheduler)) - } - - /** - * Throttles by skipping value until `skipDuration` passes and then emits the next received value. - * - * This differs from `Observable.throttleLast` in that this only tracks passage of time whereas `Observable.throttleLast` ticks at scheduled intervals. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/throttleFirst.png"> - * - * @param skipDuration - * Time to wait before sending another value after emitting last value. - * @return Observable which performs the throttle operation. - */ - def throttleFirst(skipDuration: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.throttleFirst(skipDuration.length, skipDuration.unit)) - } - - /** - * Throttles by returning the last value of each interval defined by 'intervalDuration'. - * - * This differs from `Observable.throttleFirst` in that this ticks along at a scheduled interval whereas `Observable.throttleFirst` does not tick, it just tracks passage of time. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/throttleLast.png"> - * - * @param intervalDuration - * Duration of windows within with the last value will be chosen. - * @return Observable which performs the throttle operation. - */ - def throttleLast(intervalDuration: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.throttleLast(intervalDuration.length, intervalDuration.unit)) - } - - /** - * Throttles by returning the last value of each interval defined by 'intervalDuration'. - * - * This differs from `Observable.throttleFirst` in that this ticks along at a scheduled interval whereas `Observable.throttleFirst` does not tick, it just tracks passage of time. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/throttleLast.png"> - * - * @param intervalDuration - * Duration of windows within with the last value will be chosen. - * @return Observable which performs the throttle operation. - */ - def throttleLast(intervalDuration: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.throttleLast(intervalDuration.length, intervalDuration.unit, scheduler)) - } - - /** - * Applies a timeout policy for each item emitted by the Observable, using - * the specified scheduler to run timeout timers. If the next item isn't - * observed within the specified timeout duration starting from its - * predecessor, observers are notified of a `TimeoutException`. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timeout.1.png"> - * - * @param timeout maximum duration between items before a timeout occurs - * @return the source Observable modified to notify observers of a - * `TimeoutException` in case of a timeout - */ - def timeout(timeout: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.timeout(timeout.length, timeout.unit)) - } - - /** - * Applies a timeout policy for each item emitted by the Observable, using - * the specified scheduler to run timeout timers. If the next item isn't - * observed within the specified timeout duration starting from its - * predecessor, a specified fallback Observable produces future items and - * notifications from that point on. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timeout.2.png"> - * - * @param timeout maximum duration between items before a timeout occurs - * @param other fallback Observable to use in case of a timeout - * @return the source Observable modified to switch to the fallback - * Observable in case of a timeout - */ - def timeout[U >: T](timeout: Duration, other: Observable[U]): Observable[U] = { - val otherJava: rx.Observable[_ <: U] = other.asJavaObservable - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - toScalaObservable[U](thisJava.timeout(timeout.length, timeout.unit, otherJava)) - } - - /** - * Applies a timeout policy for each item emitted by the Observable, using - * the specified scheduler to run timeout timers. If the next item isn't - * observed within the specified timeout duration starting from its - * predecessor, the observer is notified of a `TimeoutException`. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timeout.1s.png"> - * - * @param timeout maximum duration between items before a timeout occurs - * @param scheduler Scheduler to run the timeout timers on - * @return the source Observable modified to notify observers of a - * `TimeoutException` in case of a timeout - */ - def timeout(timeout: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.timeout(timeout.length, timeout.unit, scheduler.asJavaScheduler)) - } - - /** - * Applies a timeout policy for each item emitted by the Observable, using - * the specified scheduler to run timeout timers. If the next item isn't - * observed within the specified timeout duration starting from its - * predecessor, a specified fallback Observable sequence produces future - * items and notifications from that point on. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timeout.2s.png"> - * - * @param timeout maximum duration between items before a timeout occurs - * @param other Observable to use as the fallback in case of a timeout - * @param scheduler Scheduler to run the timeout timers on - * @return the source Observable modified so that it will switch to the - * fallback Observable in case of a timeout - */ - def timeout[U >: T](timeout: Duration, other: Observable[U], scheduler: Scheduler): Observable[U] = { - val otherJava: rx.Observable[_ <: U] = other.asJavaObservable - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]] - toScalaObservable[U](thisJava.timeout(timeout.length, timeout.unit, otherJava, scheduler.asJavaScheduler)) - } - - /** - * Returns an Observable that mirrors the source Observable, but emits a TimeoutException if an item emitted by - * the source Observable doesn't arrive within a window of time after the emission of the - * previous item, where that period of time is measured by an Observable that is a function - * of the previous item. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timeout3.png"> - * </p> - * Note: The arrival of the first source item is never timed out. - * - * @param timeoutSelector - * a function that returns an observable for each item emitted by the source - * Observable and that determines the timeout window for the subsequent item - * @return an Observable that mirrors the source Observable, but emits a TimeoutException if a item emitted by - * the source Observable takes longer to arrive than the time window defined by the - * selector for the previously emitted item - */ - def timeout[V](timeoutSelector: T => Observable[V]): Observable[T] = { - toScalaObservable[T](asJavaObservable.timeout({ t: T => timeoutSelector(t).asJavaObservable.asInstanceOf[rx.Observable[V]] })) - } - - /** - * Returns an Observable that mirrors the source Observable, but that switches to a fallback - * Observable if an item emitted by the source Observable doesn't arrive within a window of time - * after the emission of the previous item, where that period of time is measured by an - * Observable that is a function of the previous item. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timeout4.png"> - * </p> - * Note: The arrival of the first source item is never timed out. - * - * @param timeoutSelector - * a function that returns an observable for each item emitted by the source - * Observable and that determines the timeout window for the subsequent item - * @param other - * the fallback Observable to switch to if the source Observable times out - * @return an Observable that mirrors the source Observable, but switches to mirroring a - * fallback Observable if a item emitted by the source Observable takes longer to arrive - * than the time window defined by the selector for the previously emitted item - */ - def timeout[V, O >: T](timeoutSelector: T => Observable[V], other: Observable[O]): Observable[O] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[O]] - toScalaObservable[O](thisJava.timeout( - { t: O => timeoutSelector(t.asInstanceOf[T]).asJavaObservable.asInstanceOf[rx.Observable[V]] }, - other.asJavaObservable)) - } - - /** - * Returns an Observable that mirrors the source Observable, but emits a TimeoutException - * if either the first item emitted by the source Observable or any subsequent item - * don't arrive within time windows defined by other Observables. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timeout5.png"> - * </p> - * @param firstTimeoutSelector - * a function that returns an Observable that determines the timeout window for the - * first source item - * @param timeoutSelector - * a function that returns an Observable for each item emitted by the source - * Observable and that determines the timeout window in which the subsequent source - * item must arrive in order to continue the sequence - * @return an Observable that mirrors the source Observable, but emits a TimeoutException if either the first item or any subsequent item doesn't - * arrive within the time windows specified by the timeout selectors - */ - def timeout[U, V](firstTimeoutSelector: () => Observable[U], timeoutSelector: T => Observable[V]): Observable[T] = { - toScalaObservable[T](asJavaObservable.timeout( - { firstTimeoutSelector().asJavaObservable.asInstanceOf[rx.Observable[U]] }, - { t: T => timeoutSelector(t).asJavaObservable.asInstanceOf[rx.Observable[V]] })) - } - - /** - * Returns an Observable that mirrors the source Observable, but switches to a fallback - * Observable if either the first item emitted by the source Observable or any subsequent item - * don't arrive within time windows defined by other Observables. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timeout6.png"> - * </p> - * @param firstTimeoutSelector - * a function that returns an Observable which determines the timeout window for the - * first source item - * @param timeoutSelector - * a function that returns an Observable for each item emitted by the source - * Observable and that determines the timeout window in which the subsequent source - * item must arrive in order to continue the sequence - * @param other - * the fallback Observable to switch to if the source Observable times out - * @return an Observable that mirrors the source Observable, but switches to the `other` Observable if either the first item emitted by the source Observable or any - * subsequent item don't arrive within time windows defined by the timeout selectors - */ - def timeout[U, V, O >: T](firstTimeoutSelector: () => Observable[U], timeoutSelector: T => Observable[V], other: Observable[O]): Observable[O] = { - val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[O]] - toScalaObservable[O](thisJava.timeout( - { firstTimeoutSelector().asJavaObservable.asInstanceOf[rx.Observable[U]] }, - { t: O => timeoutSelector(t.asInstanceOf[T]).asJavaObservable.asInstanceOf[rx.Observable[V]] }, - other.asJavaObservable)) - } - - /** - * Returns an Observable that sums up the elements of this Observable. - * - * This operation is only available if the elements of this Observable are numbers, otherwise - * you will get a compilation error. - * - * @return an Observable emitting the sum of all the elements of the source Observable - * as its single item. - * - * @usecase def sum: Observable[T] - * @inheritdoc - */ - def sum[U >: T](implicit num: Numeric[U]): Observable[U] = { - foldLeft(num.zero)(num.plus) - } - - /** - * Returns an Observable that multiplies up the elements of this Observable. - * - * This operation is only available if the elements of this Observable are numbers, otherwise - * you will get a compilation error. - * - * @return an Observable emitting the product of all the elements of the source Observable - * as its single item. - * - * @usecase def product: Observable[T] - * @inheritdoc - */ - def product[U >: T](implicit num: Numeric[U]): Observable[U] = { - foldLeft(num.one)(num.times) - } - - /** - * Returns an Observable that emits only the very first item emitted by the source Observable, or - * a default value if the source Observable is empty. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/firstOrDefault.png"> - * - * @param default - * The default value to emit if the source Observable doesn't emit anything. - * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything. - * @return an Observable that emits only the very first item from the source, or a default value - * if the source Observable completes without emitting any item. - */ - def firstOrElse[U >: T](default: => U): Observable[U] = { - take(1).singleOrElse(default) - } - - /** - * Returns an Observable that emits only an `Option` with the very first item emitted by the source Observable, - * or `None` if the source Observable is empty. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/firstOrDefault.png"> - * - * @return an Observable that emits only an `Option` with the very first item from the source, or `None` - * if the source Observable completes without emitting any item. - */ - def headOption: Observable[Option[T]] = { - take(1).singleOption - } - - /** - * Returns an Observable that emits only the very first item emitted by the source Observable, or - * a default value if the source Observable is empty. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/firstOrDefault.png"> - * - * @param default - * The default value to emit if the source Observable doesn't emit anything. - * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything. - * @return an Observable that emits only the very first item from the source, or a default value - * if the source Observable completes without emitting any item. - */ - def headOrElse[U >: T](default: => U): Observable[U] = firstOrElse(default) - - /** - * Returns an Observable that emits only the very first item emitted by the source Observable, or raises an - * `NoSuchElementException` if the source Observable is empty. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/first.png"> - * - * @return an Observable that emits only the very first item emitted by the source Observable, or raises an - * `NoSuchElementException` if the source Observable is empty - * @see <a href="https://github.com/Netflix/RxJava/wiki/Filtering-Observables#wiki-first">RxJava Wiki: first()</a> - * @see "MSDN: Observable.firstAsync()" - */ - def first: Observable[T] = { - toScalaObservable[T](asJavaObservable.first) - } - - /** - * Returns an Observable that emits only the very first item emitted by the source Observable, or raises an - * `NoSuchElementException` if the source Observable is empty. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/first.png"> - * - * @return an Observable that emits only the very first item emitted by the source Observable, or raises an - * `NoSuchElementException` if the source Observable is empty - * @see <a href="https://github.com/Netflix/RxJava/wiki/Filtering-Observables#wiki-first">RxJava Wiki: first()</a> - * @see "MSDN: Observable.firstAsync()" - * @see [[Observable.first]] - */ - def head: Observable[T] = first - - /** - * Returns an Observable that emits all items except the first one, or raises an `UnsupportedOperationException` - * if the source Observable is empty. - * - * @return an Observable that emits all items except the first one, or raises an `UnsupportedOperationException` - * if the source Observable is empty. - */ - def tail: Observable[T] = { - lift { - (subscriber: Subscriber[T]) => { - var isFirst = true - Subscriber[T]( - subscriber, - (v: T) => if(isFirst) isFirst = false else subscriber.onNext(v), - e => subscriber.onError(e), - () => if(isFirst) subscriber.onError(new UnsupportedOperationException("tail of empty Observable")) else subscriber.onCompleted - ) - } - } - } - - /** - * Returns an Observable that emits the last item emitted by the source Observable or notifies observers of - * an `NoSuchElementException` if the source Observable is empty. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/last.png"> - * - * @return an Observable that emits the last item from the source Observable or notifies observers of an - * error - * @see <a href="https://github.com/Netflix/RxJava/wiki/Filtering-Observable-Operators#wiki-last">RxJava Wiki: last()</a> - * @see "MSDN: Observable.lastAsync()" - */ - def last: Observable[T] = { - toScalaObservable[T](asJavaObservable.last) - } - - /** - * Returns an Observable that emits only an `Option` with the last item emitted by the source Observable, - * or `None` if the source Observable completes without emitting any items. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/lastOrDefault.png"> - * - * @return an Observable that emits only an `Option` with the last item emitted by the source Observable, - * or `None` if the source Observable is empty - */ - def lastOption: Observable[Option[T]] = { - takeRight(1).singleOption - } - - /** - * Returns an Observable that emits only the last item emitted by the source Observable, or a default item - * if the source Observable completes without emitting any items. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/lastOrDefault.png"> - * - * @param default the default item to emit if the source Observable is empty. - * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything. - * @return an Observable that emits only the last item emitted by the source Observable, or a default item - * if the source Observable is empty - */ - def lastOrElse[U >: T](default: => U): Observable[U] = { - takeRight(1).singleOrElse(default) - } - - /** - * If the source Observable completes after emitting a single item, return an Observable that emits that - * item. If the source Observable emits more than one item or no items, notify of an `IllegalArgumentException` - * or `NoSuchElementException` respectively. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/single.png"> - * - * @return an Observable that emits the single item emitted by the source Observable - * @throws IllegalArgumentException if the source emits more than one item - * @throws NoSuchElementException if the source emits no items - * @see <a href="https://github.com/Netflix/RxJava/wiki/Observable-Utility-Operators#wiki-single-and-singleordefault">RxJava Wiki: single()</a> - * @see "MSDN: Observable.singleAsync()" - */ - def single: Observable[T] = { - toScalaObservable[T](asJavaObservable.single) - } - - /** - * If the source Observable completes after emitting a single item, return an Observable that emits an `Option` - * with that item; if the source Observable is empty, return an Observable that emits `None`. - * If the source Observable emits more than one item, throw an `IllegalArgumentException`. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/singleOrDefault.png"> - * - * @return an Observable that emits an `Option` with the single item emitted by the source Observable, or - * `None` if the source Observable is empty - * @throws IllegalArgumentException if the source Observable emits more than one item - */ - def singleOption: Observable[Option[T]] = { - val jObservableOption = map(Some(_)).asJavaObservable.asInstanceOf[rx.Observable[Option[T]]] - toScalaObservable[Option[T]](jObservableOption.singleOrDefault(None)) - } - - /** - * If the source Observable completes after emitting a single item, return an Observable that emits that - * item; if the source Observable is empty, return an Observable that emits a default item. If the source - * Observable emits more than one item, throw an `IllegalArgumentException`. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/singleOrDefault.png"> - * - * @param default a default value to emit if the source Observable emits no item. - * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything. - * @return an Observable that emits the single item emitted by the source Observable, or a default item if - * the source Observable is empty - * @throws IllegalArgumentException if the source Observable emits more than one item - */ - def singleOrElse[U >: T](default: => U): Observable[U] = { - singleOption.map { - case Some(element) => element - case None => default - } - } - - /** - * Returns an Observable that emits the items emitted by the source Observable or a specified default item - * if the source Observable is empty. - * - * <img width="640" height="305" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/defaultIfEmpty.png"> - * - * @param default the item to emit if the source Observable emits no items. This is a by-name parameter, so it is - * only evaluated if the source Observable doesn't emit anything. - * @return an Observable that emits either the specified default item if the source Observable emits no - * items, or the items emitted by the source Observable - */ - def orElse[U >: T](default: => U): Observable[U] = { - val jObservableOption = map(Some(_)).asJavaObservable.asInstanceOf[rx.Observable[Option[T]]] - val o = toScalaObservable[Option[T]](jObservableOption.defaultIfEmpty(None)) - o map { - case Some(element) => element - case None => default - } - } - - /** - * Returns an Observable that forwards all sequentially distinct items emitted from the source Observable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/distinctUntilChanged.png"> - * - * @return an Observable of sequentially distinct items - */ - def distinctUntilChanged: Observable[T] = { - toScalaObservable[T](asJavaObservable.distinctUntilChanged) - } - - /** - * Returns an Observable that forwards all items emitted from the source Observable that are sequentially - * distinct according to a key selector function. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/distinctUntilChanged.key.png"> - * - * @param keySelector - * a function that projects an emitted item to a key value which is used for deciding whether an item is sequentially - * distinct from another one or not - * @return an Observable of sequentially distinct items - */ - def distinctUntilChanged[U](keySelector: T => U): Observable[T] = { - toScalaObservable[T](asJavaObservable.distinctUntilChanged[U](keySelector)) - } - - /** - * Returns an Observable that forwards all distinct items emitted from the source Observable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/distinct.png"> - * - * @return an Observable of distinct items - */ - def distinct: Observable[T] = { - toScalaObservable[T](asJavaObservable.distinct()) - } - - /** - * Returns an Observable that forwards all items emitted from the source Observable that are distinct according - * to a key selector function. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/distinct.key.png"> - * - * @param keySelector - * a function that projects an emitted item to a key value which is used for deciding whether an item is - * distinct from another one or not - * @return an Observable of distinct items - */ - def distinct[U](keySelector: T => U): Observable[T] = { - toScalaObservable[T](asJavaObservable.distinct[U](keySelector)) - } - - /** - * Returns an Observable that counts the total number of elements in the source Observable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/count.png"> - * - * @return an Observable emitting the number of counted elements of the source Observable - * as its single item. - */ - def length: Observable[Int] = { - toScalaObservable[Integer](asJavaObservable.count()).map(_.intValue()) - } - - /** - * Returns an Observable that counts the total number of elements in the source Observable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/count.png"> - * - * @return an Observable emitting the number of counted elements of the source Observable - * as its single item. - */ - def size: Observable[Int] = length - - /** - * Retry subscription to origin Observable upto given retry count. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/retry.png"> - * - * If [[rx.lang.scala.Observer.onError]] is invoked the source Observable will be re-subscribed to as many times as defined by retryCount. - * - * Any [[rx.lang.scala.Observer.onNext]] calls received on each attempt will be emitted and concatenated together. - * - * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and - * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. - * - * @param retryCount - * Number of retry attempts before failing. - * @return Observable with retry logic. - */ - def retry(retryCount: Int): Observable[T] = { - toScalaObservable[T](asJavaObservable.retry(retryCount)) - } - - /** - * Retry subscription to origin Observable whenever onError is called (infinite retry count). - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/retry.png"> - * - * If [[rx.lang.scala.Observer.onError]] is invoked the source Observable will be re-subscribed to. - * - * Any [[rx.lang.scala.Observer.onNext]] calls received on each attempt will be emitted and concatenated together. - * - * For example, if an Observable fails on first time but emits [1, 2] then succeeds the second time and - * emits [1, 2, 3, 4, 5] then the complete output would be [1, 2, 1, 2, 3, 4, 5, onCompleted]. - * @return Observable with retry logic. - */ - def retry: Observable[T] = { - toScalaObservable[T](asJavaObservable.retry()) - } - - /** - * Returns an Observable that mirrors the source Observable, resubscribing to it if it calls `onError` - * and the predicate returns true for that specific exception and retry count. - * - * <img width="640" height="315" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/retry.png"> - * - * @param predicate the predicate that determines if a resubscription may happen in case of a specific exception and retry count - * @return the source Observable modified with retry logic - */ - def retry(predicate: (Int, Throwable) => Boolean): Observable[T] = { - val f = new Func2[java.lang.Integer, Throwable, java.lang.Boolean] { - def call(times: java.lang.Integer, e: Throwable): java.lang.Boolean = predicate(times, e) - } - toScalaObservable[T](asJavaObservable.retry(f)) - } - - /** - * Returns an Observable that repeats the sequence of items emitted by the source Observable indefinitely. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/repeat.o.png"> - * - * @return an Observable that emits the items emitted by the source Observable repeatedly and in sequence - * @see <a href="https://github.com/Netflix/RxJava/wiki/Creating-Observables#wiki-repeat">RxJava Wiki: repeat()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229428.aspx">MSDN: Observable.Repeat</a> - */ - def repeat: Observable[T] = { - toScalaObservable[T](asJavaObservable.repeat()) - } - - /** - * Returns an Observable that repeats the sequence of items emitted by the source Observable indefinitely, - * on a particular Scheduler. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/repeat.os.png"> - * - * @param scheduler the Scheduler to emit the items on - * @return an Observable that emits the items emitted by the source Observable repeatedly and in sequence - * @see <a href="https://github.com/Netflix/RxJava/wiki/Creating-Observables#wiki-repeat">RxJava Wiki: repeat()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229428.aspx">MSDN: Observable.Repeat</a> - */ - def repeat(scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.repeat(scheduler)) - } - - /** - * Returns an Observable that repeats the sequence of items emitted by the source Observable at most `count` times. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/repeat.on.png"> - * - * @param count the number of times the source Observable items are repeated, - * a count of 0 will yield an empty sequence - * @return an Observable that repeats the sequence of items emitted by the source Observable at most `count` times - * @throws IllegalArgumentException if `count` is less than zero - * @see <a href="https://github.com/Netflix/RxJava/wiki/Creating-Observables#wiki-repeat">RxJava Wiki: repeat()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229428.aspx">MSDN: Observable.Repeat</a> - */ - def repeat(count: Long): Observable[T] = { - toScalaObservable[T](asJavaObservable.repeat(count)) - } - - /** - * Returns an Observable that repeats the sequence of items emitted by the source Observable - * at most `count` times, on a particular Scheduler. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/repeat.ons.png"> - * - * @param count the number of times the source Observable items are repeated, - * a count of 0 will yield an empty sequence - * @param scheduler the `Scheduler` to emit the items on - * @return an Observable that repeats the sequence of items emitted by the source Observable at most `count` times - * on a particular Scheduler - * @see <a href="https://github.com/Netflix/RxJava/wiki/Creating-Observables#wiki-repeat">RxJava Wiki: repeat()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229428.aspx">MSDN: Observable.Repeat</a> - */ - def repeat(count: Long, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.repeat(count, scheduler)) - } - - /** - * Converts an Observable into a [[BlockingObservable]] (an Observable with blocking operators). - * - * @return a [[BlockingObservable]] version of this Observable - * @see <a href="https://github.com/Netflix/RxJava/wiki/Blocking-Observable-Operators">Blocking Observable Operators</a> - */ - @deprecated("Use `toBlocking` instead", "0.19") - def toBlockingObservable: BlockingObservable[T] = { - new BlockingObservable[T](this) - } - - /** - * Converts an Observable into a [[BlockingObservable]] (an Observable with blocking - * operators). - * - * @return a [[BlockingObservable]] version of this Observable - * @see <a href="https://github.com/Netflix/RxJava/wiki/Blocking-Observable-Operators">Blocking Observable Operators</a> - * @since 0.19 - */ - def toBlocking: BlockingObservable[T] = { - new BlockingObservable[T](this) - } - - /** - * Perform work in parallel by sharding an `Observable[T]` on a - * [[rx.lang.scala.schedulers.ComputationScheduler]] and return an `Observable[R]` with the output. - * - * @param f - * a function that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` - * @return an Observable with the output of the function executed on a [[rx.lang.scala.Scheduler]] - */ - def parallel[R](f: Observable[T] => Observable[R]): Observable[R] = { - val fJava: Func1[rx.Observable[T], rx.Observable[R]] = - (jo: rx.Observable[T]) => f(toScalaObservable[T](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable(asJavaObservable.asInstanceOf[rx.Observable[T]].parallel[R](fJava)) - } - - /** - * Perform work in parallel by sharding an `Observable[T]` on a [[rx.lang.scala.Scheduler]] and return an `Observable[R]` with the output. - * - * @param f - * a function that applies Observable operators to `Observable[T]` in parallel and returns an `Observable[R]` - * @param scheduler - * a [[rx.lang.scala.Scheduler]] to perform the work on. - * @return an Observable with the output of the function executed on a [[rx.lang.scala.Scheduler]] - */ - def parallel[R](f: Observable[T] => Observable[R], scheduler: Scheduler): Observable[R] = { - val fJava: Func1[rx.Observable[T], rx.Observable[R]] = - (jo: rx.Observable[T]) => f(toScalaObservable[T](jo)).asJavaObservable.asInstanceOf[rx.Observable[R]] - toScalaObservable(asJavaObservable.asInstanceOf[rx.Observable[T]].parallel[R](fJava, scheduler)) - } - - /** - * Converts an `Observable[Observable[T]]` into another `Observable[Observable[T]]` whose - * emitted Observables emit the same items, but the number of such Observables is restricted by `parallelObservables`. - * - * For example, if the original `Observable[Observable[T]]` emits 100 Observables and `parallelObservables` is 8, - * the items emitted by the 100 original Observables will be distributed among 8 Observables emitted by the resulting Observable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/parallelMerge.png"> - * - * This is a mechanism for efficiently processing `n` number of Observables on a smaller `m` number of resources (typically CPU cores). - * - * @param parallelObservables the number of Observables to merge into - * @return an Observable of Observables constrained in number by `parallelObservables` - */ - def parallelMerge[U](parallelObservables: Int)(implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[Observable[U]] = { - val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[U]] = o2.map(_.asJavaObservable.asInstanceOf[rx.Observable[U]]) - val o4: rx.Observable[rx.Observable[U]] = o3.asJavaObservable.asInstanceOf[rx.Observable[rx.Observable[U]]] - val o5: rx.Observable[rx.Observable[U]] = rx.Observable.parallelMerge[U](o4, parallelObservables) - toScalaObservable(o5).map(toScalaObservable[U](_)) - } - - /** - * Converts an `Observable[Observable[T]]` into another `Observable[Observable[T]]` whose - * emitted Observables emit the same items, but the number of such Observables is restricted by `parallelObservables`, - * and each runs on a defined Scheduler. - * - * For example, if the original Observable[Observable[T]]` emits 100 Observables and `parallelObservables` is 8, - * the items emitted by the 100 original Observables will be distributed among 8 Observables emitted by the resulting Observable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/parallelMerge.png"> - * - * This is a mechanism for efficiently processing n` number of Observables on a smaller `m` - * number of resources (typically CPU cores). - * - * @param parallelObservables the number of Observables to merge into - * @param scheduler the [[Scheduler]] to run each Observable on - * @return an Observable of Observables constrained in number by `parallelObservables` - */ - def parallelMerge[U](parallelObservables: Int, scheduler: Scheduler)(implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[Observable[U]] = { - val o2: Observable[Observable[U]] = this - val o3: Observable[rx.Observable[U]] = o2.map(_.asJavaObservable.asInstanceOf[rx.Observable[U]]) - val o4: rx.Observable[rx.Observable[U]] = o3.asJavaObservable.asInstanceOf[rx.Observable[rx.Observable[U]]] - val o5: rx.Observable[rx.Observable[U]] = rx.Observable.parallelMerge[U](o4, parallelObservables, scheduler) - toScalaObservable(o5).map(toScalaObservable[U](_)) - } - - /** Tests whether a predicate holds for some of the elements of this `Observable`. - * - * @param p the predicate used to test elements. - * @return an Observable emitting one single Boolean, which is `true` if the given predicate `p` - * holds for some of the elements of this Observable, and `false` otherwise. - */ - def exists(p: T => Boolean): Observable[Boolean] = { - toScalaObservable[java.lang.Boolean](asJavaObservable.exists(p)).map(_.booleanValue()) - } - - /** Tests whether this `Observable` emits no elements. - * - * @return an Observable emitting one single Boolean, which is `true` if this `Observable` - * emits no elements, and `false` otherwise. - */ - def isEmpty: Observable[Boolean] = { - toScalaObservable[java.lang.Boolean](asJavaObservable.isEmpty()).map(_.booleanValue()) - } - - def withFilter(p: T => Boolean): WithFilter[T] = { - new WithFilter[T](p, asJavaObservable) - } - - /** - * Returns an Observable that applies the given function to each item emitted by an - * Observable. - * - * @param observer the observer - * - * @return an Observable with the side-effecting behavior applied. - */ - def doOnEach(observer: Observer[T]): Observable[T] = { - toScalaObservable[T](asJavaObservable.doOnEach(observer.asJavaObserver)) - } - - /** - * Invokes an action when the source Observable calls <code>onNext</code>. - * - * @param onNext the action to invoke when the source Observable calls <code>onNext</code> - * @return the source Observable with the side-effecting behavior applied - */ - def doOnNext(onNext: T => Unit): Observable[T] = { - toScalaObservable[T](asJavaObservable.doOnNext(onNext)) - } - - /** - * Invokes an action if the source Observable calls `onError`. - * - * @param onError the action to invoke if the source Observable calls - * `onError` - * @return the source Observable with the side-effecting behavior applied - */ - def doOnError(onError: Throwable => Unit): Observable[T] = { - toScalaObservable[T](asJavaObservable.doOnError(onError)) - } - - /** - * Invokes an action when the source Observable calls `onCompleted`. - * - * @param onCompleted the action to invoke when the source Observable calls - * `onCompleted` - * @return the source Observable with the side-effecting behavior applied - */ - def doOnCompleted(onCompleted: => Unit): Observable[T] = { - toScalaObservable[T](asJavaObservable.doOnCompleted(() => onCompleted)) - } - - /** - * Returns an Observable that applies the given function to each item emitted by an - * Observable. - * - * @param onNext this function will be called whenever the Observable emits an item - * - * @return an Observable with the side-effecting behavior applied. - */ - def doOnEach(onNext: T => Unit): Observable[T] = { - toScalaObservable[T](asJavaObservable.doOnNext(onNext)) - } - - /** - * Returns an Observable that applies the given function to each item emitted by an - * Observable. - * - * @param onNext this function will be called whenever the Observable emits an item - * @param onError this function will be called if an error occurs - * - * @return an Observable with the side-effecting behavior applied. - */ - def doOnEach(onNext: T => Unit, onError: Throwable => Unit): Observable[T] = { - toScalaObservable[T](asJavaObservable.doOnEach(Observer(onNext, onError, ()=>{}))) - } - - /** - * Returns an Observable that applies the given function to each item emitted by an - * Observable. - * - * @param onNext this function will be called whenever the Observable emits an item - * @param onError this function will be called if an error occurs - * @param onCompleted the action to invoke when the source Observable calls - * - * @return an Observable with the side-effecting behavior applied. - */ - def doOnEach(onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit): Observable[T] = { - toScalaObservable[T](asJavaObservable.doOnEach(Observer(onNext, onError,onCompleted))) - } - - /** - * Modifies an Observable so that it invokes an action when it calls `onCompleted` or `onError`. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/doOnTerminate.png"> - * <p> - * This differs from `finallyDo` in that this happens **before** `onCompleted/onError` are emitted. - * - * @param onTerminate the action to invoke when the source Observable calls `onCompleted` or `onError` - * @return the source Observable with the side-effecting behavior applied - * @see <a href="https://github.com/Netflix/RxJava/wiki/Observable-Utility-Operators#wiki-doonterminate">RxJava Wiki: doOnTerminate()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229804.aspx">MSDN: Observable.Do</a> - */ - def doOnTerminate(onTerminate: => Unit): Observable[T] = { - toScalaObservable[T](asJavaObservable.doOnTerminate(() => onTerminate)) - } - - /** - * Given two Observables, mirror the one that first emits an item. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/amb.png"> - * - * @param that - * an Observable competing to react first - * @return an Observable that emits the same sequence of items as whichever of `this` or `that` first emitted an item. - */ - def amb[U >: T](that: Observable[U]): Observable[U] = { - val thisJava: rx.Observable[_ <: U] = this.asJavaObservable - val thatJava: rx.Observable[_ <: U] = that.asJavaObservable - toScalaObservable[U](rx.Observable.amb(thisJava, thatJava)) - } - - /** - * Returns an Observable that emits the items emitted by the source Observable shifted forward in time by a - * specified delay. Error notifications from the source Observable are not delayed. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/delay.png"> - * - * @param delay the delay to shift the source by - * @return the source Observable shifted in time by the specified delay - */ - def delay(delay: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.delay(delay.length, delay.unit)) - } - - /** - * Returns an Observable that emits the items emitted by the source Observable shifted forward in time by a - * specified delay. Error notifications from the source Observable are not delayed. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/delay.s.png"> - * - * @param delay the delay to shift the source by - * @param scheduler the Scheduler to use for delaying - * @return the source Observable shifted in time by the specified delay - */ - def delay(delay: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.delay(delay.length, delay.unit, scheduler)) - } - - /** - * Returns an Observable that delays the emissions of the source Observable via another Observable on a - * per-item basis. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/delay.o.png"> - * <p> - * Note: the resulting Observable will immediately propagate any `onError` notification - * from the source Observable. - * - * @param itemDelay a function that returns an Observable for each item emitted by the source Observable, which is - * then used to delay the emission of that item by the resulting Observable until the Observable - * returned from `itemDelay` emits an item - * @return an Observable that delays the emissions of the source Observable via another Observable on a per-item basis - */ - def delay(itemDelay: T => Observable[Any]): Observable[T] = { - val itemDelayJava = new Func1[T, rx.Observable[Any]] { - override def call(t: T): rx.Observable[Any] = - itemDelay(t).asJavaObservable.asInstanceOf[rx.Observable[Any]] - } - toScalaObservable[T](asJavaObservable.delay[Any](itemDelayJava)) - } - - /** - * Returns an Observable that delays the subscription to and emissions from the souce Observable via another - * Observable on a per-item basis. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/delay.oo.png"> - * <p> - * Note: the resulting Observable will immediately propagate any `onError` notification - * from the source Observable. - * - * @param subscriptionDelay a function that returns an Observable that triggers the subscription to the source Observable - * once it emits any item - * @param itemDelay a function that returns an Observable for each item emitted by the source Observable, which is - * then used to delay the emission of that item by the resulting Observable until the Observable - * returned from `itemDelay` emits an item - * @return an Observable that delays the subscription and emissions of the source Observable via another - * Observable on a per-item basis - */ - def delay(subscriptionDelay: () => Observable[Any], itemDelay: T => Observable[Any]): Observable[T] = { - val subscriptionDelayJava = new Func0[rx.Observable[Any]] { - override def call(): rx.Observable[Any] = - subscriptionDelay().asJavaObservable.asInstanceOf[rx.Observable[Any]] - } - val itemDelayJava = new Func1[T, rx.Observable[Any]] { - override def call(t: T): rx.Observable[Any] = - itemDelay(t).asJavaObservable.asInstanceOf[rx.Observable[Any]] - } - toScalaObservable[T](asJavaObservable.delay[Any, Any](subscriptionDelayJava, itemDelayJava)) - } - - /** - * Return an Observable that delays the subscription to the source Observable by a given amount of time. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/delaySubscription.png"> - * - * @param delay the time to delay the subscription - * @return an Observable that delays the subscription to the source Observable by the given amount - */ - def delaySubscription(delay: Duration): Observable[T] = { - toScalaObservable[T](asJavaObservable.delaySubscription(delay.length, delay.unit)) - } - - /** - * Return an Observable that delays the subscription to the source Observable by a given amount of time, - * both waiting and subscribing on a given Scheduler. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/delaySubscription.s.png"> - * - * @param delay the time to delay the subscription - * @param scheduler the Scheduler on which the waiting and subscription will happen - * @return an Observable that delays the subscription to the source Observable by a given - * amount, waiting and subscribing on the given Scheduler - */ - def delaySubscription(delay: Duration, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](asJavaObservable.delaySubscription(delay.length, delay.unit, scheduler)) - } - - /** - * Returns an Observable that emits the single item at a specified index in a sequence of emissions from a - * source Observbable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/elementAt.png"> - * - * @param index - * the zero-based index of the item to retrieve - * @return an Observable that emits a single item: the item at the specified position in the sequence of - * those emitted by the source Observable - * @throws IndexOutOfBoundsException - * if index is greater than or equal to the number of items emitted by the source - * Observable, or index is less than 0 - */ - def elementAt(index: Int): Observable[T] = { - toScalaObservable[T](asJavaObservable.elementAt(index)) - } - - /** - * Returns an Observable that emits the item found at a specified index in a sequence of emissions from a - * source Observable, or a default item if that index is out of range. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/elementAtOrDefault.png"> - * - * @param index - * the zero-based index of the item to retrieve - * @param default - * the default item - * @return an Observable that emits the item at the specified position in the sequence emitted by the source - * Observable, or the default item if that index is outside the bounds of the source sequence - * @throws IndexOutOfBoundsException - * if `index` is less than 0 - */ - def elementAtOrDefault[U >: T](index: Int, default: U): Observable[U] = { - val thisJava = asJavaObservable.asInstanceOf[rx.Observable[U]] - toScalaObservable[U](thisJava.elementAtOrDefault(index, default)) - } - - /** - * Return an Observable that emits a single Map containing all items emitted by the source Observable, - * mapped by the keys returned by a specified `keySelector` function. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/toMap.png"> - * <p> - * If more than one source item maps to the same key, the Map will contain the latest of those items. - * - * @param keySelector - * the function that extracts the key from a source item to be used in the Map - * @return an Observable that emits a single item: a Map containing the mapped items from the source - * Observable - */ - def toMap[K] (keySelector: T => K): Observable[Map[K, T]]= { - val thisJava = asJavaObservable.asInstanceOf[rx.Observable[T]] - val o: rx.Observable[util.Map[K, T]] = thisJava.toMap[K](keySelector) - toScalaObservable[util.Map[K,T]](o).map(m => m.toMap) - } - - /** - * Return an Observable that emits a single Map containing values corresponding to items emitted by the - * source Observable, mapped by the keys returned by a specified {@code keySelector} function. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/toMap.png"> - * <p> - * If more than one source item maps to the same key, the Map will contain a single entry that - * corresponds to the latest of those items. - * - * @param keySelector - * the function that extracts the key from a source item to be used in the Map - * @param valueSelector - * the function that extracts the value from a source item to be used in the Map - * @return an Observable that emits a single item: a HashMap containing the mapped items from the source - * Observable - */ - def toMap[K, V] (keySelector: T => K, valueSelector: T => V) : Observable[Map[K, V]] = { - val thisJava = asJavaObservable.asInstanceOf[rx.Observable[T]] - val o: rx.Observable[util.Map[K, V]] = thisJava.toMap[K, V](keySelector, valueSelector) - toScalaObservable[util.Map[K, V]](o).map(m => m.toMap) - } - - /** - * Return an Observable that emits a single Map, returned by a specified {@code mapFactory} function, that - * contains keys and values extracted from the items emitted by the source Observable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/toMap.png"> - * - * @param keySelector - * the function that extracts the key from a source item to be used in the Map - * @param valueSelector - * the function that extracts the value from the source items to be used as value in the Map - * @param mapFactory - * the function that returns a Map instance to be used - * @return an Observable that emits a single item: a Map that contains the mapped items emitted by the - * source Observable - */ - def toMap[K, V] (keySelector: T => K, valueSelector: T => V, mapFactory: () => Map[K, V]): Observable[Map[K, V]] = { - val thisJava = asJavaObservable.asInstanceOf[rx.Observable[T]] - val o: rx.Observable[util.Map[K, V]] = thisJava.toMap[K, V](keySelector, valueSelector) - toScalaObservable[util.Map[K, V]](o).map(m => mapFactory() ++ m.toMap) - } - - /** - * Returns an Observable that emits a Boolean value that indicates whether `this` and `that` Observable sequences are the - * same by comparing the items emitted by each Observable pairwise. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/sequenceEqual.png"> - * - * Note: this method uses `==` to compare elements. It's a bit different from RxJava which uses `Object.equals`. - * - * @param that the Observable to compare - * @return an Observable that emits a `Boolean` value that indicates whether the two sequences are the same - */ - def sequenceEqual[U >: T](that: Observable[U]): Observable[Boolean] = { - sequenceEqualWith(that)(_ == _) - } - - /** - * Returns an Observable that emits a Boolean value that indicates whether `this` and `that` Observable sequences are the - * same by comparing the items emitted by each Observable pairwise based on the results of a specified `equality` function. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/sequenceEqual.png"> - * - * @param that the Observable to compare - * @param equality a function used to compare items emitted by each Observable - * @return an Observable that emits a `Boolean` value that indicates whether the two sequences are the same based on the `equality` function. - */ - def sequenceEqualWith[U >: T](that: Observable[U])(equality: (U, U) => Boolean): Observable[Boolean] = { - val thisJava: rx.Observable[_ <: U] = this.asJavaObservable - val thatJava: rx.Observable[_ <: U] = that.asJavaObservable - val equalityJava: Func2[_ >: U, _ >: U, java.lang.Boolean] = equality - toScalaObservable[java.lang.Boolean](rx.Observable.sequenceEqual[U](thisJava, thatJava, equalityJava)).map(_.booleanValue) - } - - /** - * Returns an Observable that emits records of the time interval between consecutive items emitted by the - * source Obsegrvable. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timeInterval.png"> - * - * @return an Observable that emits time interval information items - */ - def timeInterval: Observable[(Duration, T)] = { - toScalaObservable(asJavaObservable.timeInterval()) - .map(inv => (Duration(inv.getIntervalInMilliseconds, MILLISECONDS), inv.getValue)) - } - - /** - * Returns an Observable that emits records of the time interval between consecutive items emitted by the - * source Observable, where this interval is computed on a specified Scheduler. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timeInterval.s.png"> - * - * @param scheduler the [[Scheduler]] used to compute time intervals - * @return an Observable that emits time interval information items - */ - def timeInterval(scheduler: Scheduler): Observable[(Duration, T)] = { - toScalaObservable(asJavaObservable.timeInterval(scheduler.asJavaScheduler)) - .map(inv => (Duration(inv.getIntervalInMilliseconds, MILLISECONDS), inv.getValue)) - } - - /** - * Lift a function to the current Observable and return a new Observable that when subscribed to will pass - * the values of the current Observable through the function. - * <p> - * In other words, this allows chaining Observers together on an Observable for acting on the values within - * the Observable. - * {{{ - * observable.map(...).filter(...).take(5).lift(new ObserverA()).lift(new ObserverB(...)).subscribe() - * }}} - * - * @param operator - * @return an Observable that emits values that are the result of applying the bind function to the values - * of the current Observable - */ - def lift[R](operator: Subscriber[R] => Subscriber[T]): Observable[R] = { - toScalaObservable(asJavaObservable.lift(toJavaOperator[T, R](operator))) - } - - /** - * Converts the source `Observable[T]` into an `Observable[Observable[T]]` that emits the source Observable as its single emission. - * - * <img width="640" height="350" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/nest.png"> - * - * @return an Observable that emits a single item: the source Observable - */ - def nest: Observable[Observable[T]] = { - toScalaObservable(asJavaObservable.nest).map(toScalaObservable[T](_)) - } - - /** - * Subscribes to the [[Observable]] and receives notifications for each element. - * - * Alias to `subscribe(T => Unit)`. - * - * @param onNext function to execute for each item. - * @throws IllegalArgumentException if `onNext` is null - * @since 0.19 - */ - def foreach(onNext: T => Unit): Unit = { - asJavaObservable.subscribe(onNext) - } - - /** - * Subscribes to the [[Observable]] and receives notifications for each element and error events. - * - * Alias to `subscribe(T => Unit, Throwable => Unit)`. - * - * @param onNext function to execute for each item. - * @param onError function to execute when an error is emitted. - * @throws IllegalArgumentException if `onNext` is null, or if `onError` is null - * @since 0.19 - */ - def foreach(onNext: T => Unit, onError: Throwable => Unit): Unit = { - asJavaObservable.subscribe(onNext, onError) - } - - /** - * Subscribes to the [[Observable]] and receives notifications for each element and the terminal events. - * - * Alias to `subscribe(T => Unit, Throwable => Unit, () => Unit)`. - * - * @param onNext function to execute for each item. - * @param onError function to execute when an error is emitted. - * @param onComplete function to execute when completion is signalled. - * @throws IllegalArgumentException if `onNext` is null, or if `onError` is null, or if `onComplete` is null - * @since 0.19 - */ - def foreach(onNext: T => Unit, onError: Throwable => Unit, onComplete: () => Unit): Unit = { - asJavaObservable.subscribe(onNext, onError, onComplete) - } - - /** - * Pivots a sequence of `(K1, Observable[(K2, Observable[U])])`s emitted by an `Observable` so as to swap the group - * and and the set on which their items are grouped. - * <p> - * <img width="640" height="580" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/pivot.png"> - * - * For example an `Observable` such as `this = Observable[(String, Observable[(Boolean, Observable[Integer])])`: - * <ul> - * <li>o1.odd: 1, 3, 5, 7, 9 on Thread 1</li> - * <li>o1.even: 2, 4, 6, 8, 10 on Thread 1</li> - * <li>o2.odd: 11, 13, 15, 17, 19 on Thread 2</li> - * <li>o2.even: 12, 14, 16, 18, 20 on Thread 2</li> - * </ul> - * is pivoted to become `this = Observable[(Boolean, Observable[(String, Observable[Integer])])`: - * - * <ul> - * <li>odd.o1: 1, 3, 5, 7, 9 on Thread 1</li> - * <li>odd.o2: 11, 13, 15, 17, 19 on Thread 2</li> - * <li>even.o1: 2, 4, 6, 8, 10 on Thread 1</li> - * <li>even.o2: 12, 14, 16, 18, 20 on Thread 2</li> - * </ul> - * <p> - * <img width="640" height="1140" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/pivot.ex.png"> - * <p> - * <em>Note:</em> A `(K, Observable[_])` will cache the items it is to emit until such time as it - * is subscribed to. For this reason, in order to avoid memory leaks, you should not simply ignore those - * `(K, Observable[_])`s that do not concern you. Instead, you can signal to them that they may - * discard their buffers by applying an operator like `take(0)` to them. - * - * @return an `Observable`containing a stream of nested `(K1, Observable[(K2, Observable[U])])`s with swapped - * inner-outer keys. - */ - def pivot[U, K1, K2](implicit evidence: Observable[T] <:< Observable[(K1, Observable[(K2, Observable[U])])]): Observable[(K2, Observable[(K1, Observable[U])])] = { - import rx.observables.{GroupedObservable => JGroupedObservable} - val f1 = new Func1[(K1, Observable[(K2, Observable[U])]), JGroupedObservable[K1, JGroupedObservable[K2, U]]]() { - override def call(t1: (K1, Observable[(K2, Observable[U])])): JGroupedObservable[K1, JGroupedObservable[K2, U]] = { - val jo = t1._2.asJavaObservable.asInstanceOf[rx.Observable[(K2, Observable[U])]].map[JGroupedObservable[K2, U]](new Func1[(K2, Observable[U]), JGroupedObservable[K2, U]]() { - override def call(t2: (K2, Observable[U])): JGroupedObservable[K2, U] = { - JGroupedObservable.from(t2._1, t2._2.asJavaObservable.asInstanceOf[rx.Observable[U]]) - } - }) - JGroupedObservable.from(t1._1, jo) - } - } - val o1: Observable[(K1, Observable[(K2, Observable[U])])] = this - val o2 = toScalaObservable[JGroupedObservable[K2, JGroupedObservable[K1, U]]](rx.Observable.pivot(o1.asJavaObservable.map(f1))) - o2.map { - (jgo1: JGroupedObservable[K2, JGroupedObservable[K1, U]]) => { - val jo = jgo1.map[(K1, Observable[U])](new Func1[JGroupedObservable[K1, U], (K1, Observable[U])]() { - override def call(jgo2: JGroupedObservable[K1, U]): (K1, Observable[U]) = (jgo2.getKey, toScalaObservable[U](jgo2)) - }) - (jgo1.getKey, toScalaObservable[(K1, Observable[U])](jo)) - } - } - } - - /** - * Returns an Observable that counts the total number of items emitted by the source Observable and emits this count as a 64-bit Long. - * - * <img width="640" height="310" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/longCount.png"> - * - * @return an Observable that emits a single item: the number of items emitted by the source Observable as a 64-bit Long item - */ - def longCount: Observable[Long] = { - toScalaObservable[java.lang.Long](asJavaObservable.longCount()).map(_.longValue()) - } - - /** - * Returns an Observable that emits a single `Map` that contains an `Seq` of items emitted by the - * source Observable keyed by a specified `keySelector` function. - * - * <img width="640" height="305" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/toMultiMap.png"> - * - * @param keySelector the function that extracts the key from the source items to be used as key in the HashMap - * @return an Observable that emits a single item: a `Map` that contains an `Seq` of items mapped from - * the source Observable - */ - def toMultimap[K](keySelector: T => K): Observable[scala.collection.Map[K, Seq[T]]] = { - toMultimap(keySelector, k => k) - } - - /** - * Returns an Observable that emits a single `Map` that contains an `Seq` of values extracted by a - * specified `valueSelector` function from items emitted by the source Observable, keyed by a - * specified `keySelector` function. - * - * <img width="640" height="305" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/toMultiMap.png"> - * - * @param keySelector the function that extracts a key from the source items to be used as key in the HashMap - * @param valueSelector the function that extracts a value from the source items to be used as value in the HashMap - * @return an Observable that emits a single item: a `Map` that contains an `Seq` of items mapped from - * the source Observable - */ - def toMultimap[K, V](keySelector: T => K, valueSelector: T => V): Observable[scala.collection.Map[K, Seq[V]]] = { - toMultimap(keySelector, valueSelector, () => mutable.Map[K, mutable.Buffer[V]]()) - } - - /** - * Returns an Observable that emits a single `mutable.Map[K, mutable.Buffer[V]]`, returned by a specified `mapFactory` function, that - * contains values, extracted by a specified `valueSelector` function from items emitted by the source Observable and - * keyed by the `keySelector` function. `mutable.Map[K, B]` is the same instance create by `mapFactory`. - * - * <img width="640" height="305" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/toMultiMap.png"> - * - * @param keySelector the function that extracts a key from the source items to be used as the key in the Map - * @param valueSelector the function that extracts a value from the source items to be used as the value in the Map - * @param mapFactory he function that returns a `mutable.Map[K, mutable.Buffer[V]]` instance to be used - * @return an Observable that emits a single item: a `mutable.Map[K, mutable.Buffer[V]]` that contains items mapped - * from the source Observable - */ - def toMultimap[K, V, M <: mutable.Map[K, mutable.Buffer[V]]](keySelector: T => K, valueSelector: T => V, mapFactory: () => M): Observable[M] = { - toMultimap[K, V, mutable.Buffer[V], M](keySelector, valueSelector, mapFactory, k => mutable.Buffer[V]()) - } - - /** - * Returns an Observable that emits a single `mutable.Map[K, B]`, returned by a specified `mapFactory` function, that - * contains values extracted by a specified `valueSelector` function from items emitted by the source Observable, and - * keyed by the `keySelector` function. `mutable.Map[K, B]` is the same instance create by `mapFactory`. - * - * <img width="640" height="305" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/toMultiMap.png"> - * - * @param keySelector the function that extracts a key from the source items to be used as the key in the Map - * @param valueSelector the function that extracts a value from the source items to be used as the value in the Map - * @param mapFactory the function that returns a Map instance to be used - * @param bufferFactory the function that returns a `mutable.Buffer[V]` instance for a particular key to be used in the Map - * @return an Observable that emits a single item: a `mutable.Map[K, B]` that contains mapped items from the source Observable. - */ - def toMultimap[K, V, B <: mutable.Buffer[V], M <: mutable.Map[K, B]](keySelector: T => K, valueSelector: T => V, mapFactory: () => M, bufferFactory: K => B): Observable[M] = { - // It's complicated to convert `mutable.Map[K, mutable.Buffer[V]]` to `java.util.Map[K, java.util.Collection[V]]`, - // so RxScala implements `toMultimap` directly. - // Choosing `mutable.Buffer/Map` is because `append/update` is necessary to implement an efficient `toMultimap`. - lift { - (subscriber: Subscriber[M]) => { - val map = mapFactory() - Subscriber[T]( - subscriber, - (t: T) => { - val key = keySelector(t) - val values = map.get(key) match { - case Some(v) => v - case None => bufferFactory(key) - } - values += valueSelector(t) - map += key -> values: Unit - }, - e => subscriber.onError(e), - () => { - subscriber.onNext(map) - subscriber.onCompleted() - } - ) - } - } - } - - /** - * Returns an Observable that emits a single item, a collection composed of all the items emitted by - * the source Observable. - * - * 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. - * - * @tparam Col the collection type to build. - * @return an Observable that emits a single item, a collection containing all of the items emitted by - * the source Observable. - */ - def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Observable[Col[T @uncheckedVariance]] = { - lift { - (subscriber: Subscriber[Col[T]]) => { - val b = cbf() - Subscriber[T]( - subscriber, - (t: T) => { - b += t: Unit - }, - e => subscriber.onError(e), - () => { - subscriber.onNext(b.result) - subscriber.onCompleted() - } - ) - } - } - } - - /** - * Returns an Observable that emits a single item, a `Traversable` composed of all the items emitted by - * the source Observable. - * - * 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 `Traversable` containing all of the items emitted by - * the source Observable. - */ - def toTraversable: Observable[Traversable[T]] = to[Traversable] - - /** - * Returns an Observable that emits a single item, a `List` composed of all the items emitted by - * the source Observable. - * - * 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. - */ - def toList: Observable[List[T]] = to[List] - - /** - * Returns an Observable that emits a single item, an `Iterable` composed of all the items emitted by - * the source Observable. - * - * 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, an `Iterable` containing all of the items emitted by - * the source Observable. - */ - def toIterable: Observable[Iterable[T]] = to[Iterable] - - /** - * Returns an Observable that emits a single item, an `Iterator` composed of all the items emitted by - * the source Observable. - * - * 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, an `Iterator` containing all of the items emitted by - * the source Observable. - */ - def toIterator: Observable[Iterator[T]] = toIterable.map(_.iterator) - - /** - * Returns an Observable that emits a single item, a `Stream` composed of all the items emitted by - * the source Observable. - * - * 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 `Stream` containing all of the items emitted by - * the source Observable. - */ - def toStream: Observable[Stream[T]] = to[Stream] - - /** - * Returns an Observable that emits a single item, an `IndexedSeq` composed of all the items emitted by - * the source Observable. - * - * 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, an `IndexedSeq` containing all of the items emitted by - * the source Observable. - */ - def toIndexedSeq: Observable[immutable.IndexedSeq[T]] = to[immutable.IndexedSeq] - - /** - * Returns an Observable that emits a single item, a `Vector` composed of all the items emitted by - * the source Observable. - * - * 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 `Vector` containing all of the items emitted by - * the source Observable. - */ - def toVector: Observable[Vector[T]] = to[Vector] - - /** - * Returns an Observable that emits a single item, a `Buffer` composed of all the items emitted by - * the source Observable. - * - * 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 `Buffer` containing all of the items emitted by - * the source Observable. - */ - def toBuffer[U >: T]: Observable[mutable.Buffer[U]] = { // use U >: T because Buffer is invariant - val us: Observable[U] = this - us.to[ArrayBuffer] - } - - /** - * Returns an Observable that emits a single item, a `Set` composed of all the items emitted by - * the source Observable. - * - * 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 `Set` containing all of the items emitted by - * the source Observable. - */ - def toSet[U >: T]: Observable[immutable.Set[U]] = { // use U >: T because Set is invariant - val us: Observable[U] = this - us.to[immutable.Set] - } - - /** - * Returns an Observable that emits a single item, an `Array` composed of all the items emitted by - * the source Observable. - * - * 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, an `Array` containing all of the items emitted by - * the source Observable. - */ - def toArray[U >: T : ClassTag]: Observable[Array[U]] = // use U >: T because Array is invariant - toBuffer[U].map(_.toArray) -} - -/** - * Provides various ways to construct new Observables. - */ -object Observable { - import scala.collection.JavaConverters._ - import scala.collection.immutable.Range - import scala.concurrent.duration.Duration - import scala.concurrent.{Future, ExecutionContext} - import scala.util.{Success, Failure} - import ImplicitFunctionConversions._ - import JavaConversions._ - import rx.lang.scala.subjects.AsyncSubject - - private[scala] - def jObsOfListToScObsOfSeq[T](jObs: rx.Observable[_ <: java.util.List[T]]): Observable[Seq[T]] = { - val oScala1: Observable[java.util.List[T]] = new Observable[java.util.List[T]]{ val asJavaObservable = jObs } - oScala1.map((lJava: java.util.List[T]) => lJava.asScala) - } - - private[scala] - def jObsOfJObsToScObsOfScObs[T](jObs: rx.Observable[_ <: rx.Observable[_ <: T]]): Observable[Observable[T]] = { - val oScala1: Observable[rx.Observable[_ <: T]] = new Observable[rx.Observable[_ <: T]]{ val asJavaObservable = jObs } - oScala1.map((oJava: rx.Observable[_ <: T]) => oJava) - } - - /** - * Creates an Observable that will execute the given function when an [[rx.lang.scala.Observer]] subscribes to it. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/create.png"> - * - * Write the function you pass to `create` so that it behaves as an Observable: It - * should invoke the Observer's [[rx.lang.scala.Observer.onNext onNext]], [[rx.lang.scala.Observer.onError onError]], and [[rx.lang.scala.Observer.onCompleted onCompleted]] methods - * appropriately. - * - * See <a href="http://go.microsoft.com/fwlink/?LinkID=205219">Rx Design Guidelines (PDF)</a> - * for detailed information. - * - * - * @tparam T - * the type of the items that this Observable emits. - * @param func - * a function that accepts an `Observer[T]`, invokes its `onNext`, `onError`, and `onCompleted` methods - * as appropriate, and returns a [[rx.lang.scala.Subscription]] to allow the Observer to - * canceling the subscription. - * @return - * an Observable that, when an [[rx.lang.scala.Observer]] subscribes to it, will execute the given function. - */ - def create[T](func: Observer[T] => Subscription): Observable[T] = { - Observable( - (subscriber: Subscriber[T]) => { - val s = func(subscriber) - if (s != null && s != subscriber) { - subscriber.add(s) - } - } - ) - } - - /* - Note: It's dangerous to have two overloads where one takes an `Observer[T] => Subscription` - function and the other takes a `Subscriber[T] => Unit` function, because expressions like - `o => Subscription{}` have both of these types. - So we call the old create method "create", and the new create method "apply". - Try it out yourself here: - def foo[T]: Unit = { - val fMeant: Observer[T] => Subscription = o => Subscription{} - val fWrong: Subscriber[T] => Unit = o => Subscription{} - } - */ - - /** - * Returns an Observable that will execute the specified function when someone subscribes to it. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/create.png"> - * - * Write the function you pass so that it behaves as an Observable: It should invoke the - * Subscriber's `onNext`, `onError`, and `onCompleted` methods appropriately. - * - * You can `add` custom [[Subscription]]s to [[Subscriber]]. These [[Subscription]]s will be called - * <ul> - * <li>when someone calls `unsubscribe`.</li> - * <li>after `onCompleted` or `onError`.</li> - * </ul> - * - * See <a href="http://go.microsoft.com/fwlink/?LinkID=205219">Rx Design Guidelines (PDF)</a> for detailed - * information. - * - * See `<a href="https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala">RxScalaDemo</a>.createExampleGood` - * and `<a href="https://github.com/Netflix/RxJava/blob/master/language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala">RxScalaDemo</a>.createExampleGood2`. - * - * @tparam T - * the type of the items that this Observable emits - * @param f - * a function that accepts a `Subscriber[T]`, and invokes its `onNext`, - * `onError`, and `onCompleted` methods as appropriate - * @return an Observable that, when someone subscribes to it, will execute the specified - * function - */ - def apply[T](f: Subscriber[T] => Unit): Observable[T] = { - toScalaObservable(rx.Observable.create(f)) - } - - /** - * Returns an Observable that invokes an [[rx.lang.scala.Observer]]'s [[rx.lang.scala.Observer.onError onError]] - * method when the Observer subscribes to it. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/error.png"> - * - * @param exception - * the particular error to report - * @tparam T - * the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the [[rx.lang.scala.Observer]]'s [[rx.lang.scala.Observer.onError onError]] - * method when the Observer subscribes to it - */ - def error[T](exception: Throwable): Observable[T] = { - toScalaObservable[T](rx.Observable.error(exception)) - } - - /** - * Returns an Observable that invokes an `Observer`'s `onError` method on the - * specified Scheduler. - * - * <img width="640" height="190" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/error.s.png"> - * - * @param exception the particular Throwable to pass to `onError` - * @param scheduler the Scheduler on which to call `onError` - * @tparam T the type of the items (ostensibly) emitted by the Observable - * @return an Observable that invokes the `Observer`'s `onError` method, on the specified Scheduler - */ - def error[T](exception: Throwable, scheduler: Scheduler): Observable[T] = { - toScalaObservable[T](rx.Observable.error(exception, scheduler)) - } - - /** - * Returns an Observable that emits no data to the [[rx.lang.scala.Observer]] and - * immediately invokes its [[rx.lang.scala.Observer#onCompleted onCompleted]] method - * with the specified scheduler. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/empty.s.png"> - * - * @return an Observable that returns no data to the [[rx.lang.scala.Observer]] and - * immediately invokes the [[rx.lang.scala.Observer]]r's - * [[rx.lang.scala.Observer#onCompleted onCompleted]] method with the - * specified scheduler - * @see <a href="https://github.com/Netflix/RxJava/wiki/Creating-Observables#empty-error-and-never">RxJava Wiki: empty()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229066.aspx">MSDN: Observable.Empty Method (IScheduler)</a> - */ - def empty: Observable[Nothing] = { - toScalaObservable(rx.Observable.empty[Nothing]()) - } - - /** - * Returns an Observable that emits no data to the [[rx.lang.scala.Observer]] and - * immediately invokes its [[rx.lang.scala.Observer#onCompleted onCompleted]] method - * with the specified scheduler. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/empty.s.png"> - * - * @param scheduler the scheduler to call the - [[rx.lang.scala.Observer#onCompleted onCompleted]] method - * @return an Observable that returns no data to the [[rx.lang.scala.Observer]] and - * immediately invokes the [[rx.lang.scala.Observer]]r's - * [[rx.lang.scala.Observer#onCompleted onCompleted]] method with the - * specified scheduler - * @see <a href="https://github.com/Netflix/RxJava/wiki/Creating-Observables#empty-error-and-never">RxJava Wiki: empty()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229066.aspx">MSDN: Observable.Empty Method (IScheduler)</a> - */ - def empty(scheduler: Scheduler): Observable[Nothing] = { - toScalaObservable(rx.Observable.empty[Nothing](scalaSchedulerToJavaScheduler(scheduler))) - } - - /** - * Converts a sequence of values into an Observable. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/from.png"> - * - * Implementation note: the entire array will be immediately emitted each time an [[rx.lang.scala.Observer]] subscribes. - * Since this occurs before the [[rx.lang.scala.Subscription]] is returned, - * it in not possible to unsubscribe from the sequence before it completes. - * - * @param items - * the source Array - * @tparam T - * 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 - */ - def items[T](items: T*): Observable[T] = { - toScalaObservable[T](rx.Observable.from(items.toIterable.asJava)) - } - - /** Returns an Observable emitting the value produced by the Future as its single item. - * If the future fails, the Observable will fail as well. - * - * @param f Future whose value ends up in the resulting Observable - * @return an Observable completed after producing the value of the future, or with an exception - */ - def from[T](f: Future[T])(implicit execContext: ExecutionContext): Observable[T] = { - val s = AsyncSubject[T]() - f.onComplete { - case Failure(e) => - s.onError(e) - case Success(c) => - s.onNext(c) - s.onCompleted() - } - s - } - - /** - * Converts an `Iterable` into an Observable. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/from.png"> - * - * Note: the entire iterable sequence is immediately emitted each time an - * Observer subscribes. Since this occurs before the - * `Subscription` is returned, it is not possible to unsubscribe from - * the sequence before it completes. - * - * @param iterable the source `Iterable` sequence - * @tparam T the type of items in the `Iterable` sequence and the - * type of items to be emitted by the resulting Observable - * @return an Observable that emits each item in the source `Iterable` - * sequence - */ - def from[T](iterable: Iterable[T]): Observable[T] = { - toScalaObservable(rx.Observable.from(iterable.asJava)) - } - - /** - * - * @param iterable the source `Iterable` sequence - * @param scheduler the scheduler to use - * @tparam T the type of items in the `Iterable` sequence and the - * type of items to be emitted by the resulting Observable - * @return an Observable that emits each item in the source `Iterable` - * sequence - */ - def from[T](iterable: Iterable[T], scheduler: Scheduler): Observable[T] = { - toScalaObservable(rx.Observable.from(iterable.asJava, scheduler.asJavaScheduler)) - } - - - /** - * Returns an Observable that calls an Observable factory to create its Observable for each - * new Observer that subscribes. That is, for each subscriber, the actual Observable is determined - * by the factory function. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/defer.png"> - * - * 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 [[rx.lang.scala.Observer]] to easily - * obtain updates or a refreshed version of the sequence. - * - * @param observable - * the Observable factory function to invoke for each [[rx.lang.scala.Observer]] that - * subscribes to the resulting Observable - * @tparam T - * the type of the items emitted by the Observable - * @return an Observable whose [[rx.lang.scala.Observer]]s trigger an invocation of the given Observable - * factory function - */ - def defer[T](observable: => Observable[T]): Observable[T] = { - toScalaObservable[T](rx.Observable.defer[T](() => observable.asJavaObservable)) - } - - /** - * Returns an Observable that never sends any items or notifications to an [[rx.lang.scala.Observer]]. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/never.png"> - * - * This Observable is useful primarily for testing purposes. - * - * @return an Observable that never sends any items or notifications to an [[rx.lang.scala.Observer]] - */ - def never: Observable[Nothing] = { - toScalaObservable[Nothing](rx.Observable.never()) - } - - /** - * Given 3 observables, returns an observable that emits Tuples of 3 elements each. - * The first emitted Tuple will contain the first element of each source observable, - * the second Tuple the second element of each source observable, and so on. - * - * @return an Observable that emits the zipped Observables - */ - def zip[A, B, C](obA: Observable[A], obB: Observable[B], obC: Observable[C]): Observable[(A, B, C)] = { - toScalaObservable[(A, B, C)](rx.Observable.zip[A, B, C, (A, B, C)](obA.asJavaObservable, obB.asJavaObservable, obC.asJavaObservable, (a: A, b: B, c: C) => (a, b, c))) - } - - /** - * Given 4 observables, returns an observable that emits Tuples of 4 elements each. - * The first emitted Tuple will contain the first element of each source observable, - * the second Tuple the second element of each source observable, and so on. - * - * @return an Observable that emits the zipped Observables - */ - def zip[A, B, C, D](obA: Observable[A], obB: Observable[B], obC: Observable[C], obD: Observable[D]): Observable[(A, B, C, D)] = { - toScalaObservable[(A, B, C, D)](rx.Observable.zip[A, B, C, D, (A, B, C, D)](obA.asJavaObservable, obB.asJavaObservable, obC.asJavaObservable, obD.asJavaObservable, (a: A, b: B, c: C, d: D) => (a, b, c, d))) - } - - /** - * Given an Observable emitting `N` source observables, returns an observable that - * emits Seqs of `N` elements each. - * The first emitted Seq will contain the first element of each source observable, - * the second Seq the second element of each source observable, and so on. - * - * Note that the returned Observable will only start emitting items once the given - * `Observable[Observable[T]]` has completed, because otherwise it cannot know `N`. - * - * @param observables - * An Observable emitting N source Observables - * @return an Observable that emits the zipped Seqs - */ - def zip[T](observables: Observable[Observable[T]]): Observable[Seq[T]] = { - val f: FuncN[Seq[T]] = (args: Seq[java.lang.Object]) => { - val asSeq: Seq[Object] = args.toSeq - asSeq.asInstanceOf[Seq[T]] - } - val list = observables.map(_.asJavaObservable).asJavaObservable - val o = rx.Observable.zip(list, f) - toScalaObservable[Seq[T]](o) - } - - /** - * Emits `0`, `1`, `2`, `...` with a delay of `duration` between consecutive numbers. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/interval.png"> - * - * @param duration - * duration between two consecutive numbers - * @return An Observable that emits a number each time interval. - */ - def interval(duration: Duration): Observable[Long] = { - toScalaObservable[java.lang.Long](rx.Observable.interval(duration.length, duration.unit)).map(_.longValue()) - } - - /** - * Emits `0`, `1`, `2`, `...` with a delay of `duration` between consecutive numbers. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/interval.png"> - * - * @param period - * duration between two consecutive numbers - * @param scheduler - * the scheduler to use - * @return An Observable that emits a number each time interval. - */ - def interval(period: Duration, scheduler: Scheduler): Observable[Long] = { - toScalaObservable[java.lang.Long](rx.Observable.interval(period.length, period.unit, scheduler)).map(_.longValue()) - } - - /** - * Return an Observable that emits a 0L after the {@code initialDelay} and ever increasing - * numbers after each {@code period} of time thereafter, on a specified Scheduler. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timer.ps.png"> - * - * @param initialDelay - * the initial delay time to wait before emitting the first value of 0L - * @param period - * the period of time between emissions of the subsequent numbers - * @return an Observable that emits a 0L after the { @code initialDelay} and ever increasing - * numbers after each { @code period} of time thereafter, while running on the given { @code scheduler} - */ - def timer(initialDelay: Duration, period: Duration): Observable[Long] = { - toScalaObservable[java.lang.Long](rx.Observable.timer(initialDelay.toNanos, period.toNanos, duration.NANOSECONDS)).map(_.longValue()) - } - - /** - * Return an Observable that emits a 0L after the {@code initialDelay} and ever increasing - * numbers after each {@code period} of time thereafter, on a specified Scheduler. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timer.ps.png"> - * - * @param initialDelay - * the initial delay time to wait before emitting the first value of 0L - * @param period - * the period of time between emissions of the subsequent numbers - * @param scheduler - * the scheduler on which the waiting happens and items are emitted - * @return an Observable that emits a 0L after the { @code initialDelay} and ever increasing - * numbers after each { @code period} of time thereafter, while running on the given { @code scheduler} - */ - def timer(initialDelay: Duration, period: Duration, scheduler: Scheduler): Observable[Long] = { - toScalaObservable[java.lang.Long](rx.Observable.timer(initialDelay.toNanos, period.toNanos, duration.NANOSECONDS, scheduler)).map(_.longValue()) - } - - /** - * Returns an Observable that emits `0L` after a specified delay, and then completes. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timer.png"> - * - * @param delay the initial delay before emitting a single `0L` - * @return Observable that emits `0L` after a specified delay, and then completes - */ - def timer(delay: Duration): Observable[Long] = { - toScalaObservable[java.lang.Long](rx.Observable.timer(delay.length, delay.unit)).map(_.longValue()) - } - - /** - * Returns an Observable that emits `0L` after a specified delay, on a specified Scheduler, and then - * completes. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/timer.s.png"> - * - * @param delay the initial delay before emitting a single `0L` - * @param scheduler the Scheduler to use for scheduling the item - * @return Observable that emits `0L` after a specified delay, on a specified Scheduler, and then completes - */ - def timer(delay: Duration, scheduler: Scheduler): Observable[Long] = { - toScalaObservable[java.lang.Long](rx.Observable.timer(delay.length, delay.unit, scheduler)).map(_.longValue()) - } - - /** - * Constructs an Observable that creates a dependent resource object. - * <p> - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/using.png"> - * - * @param resourceFactory the factory function to create a resource object that depends on the Observable - * @param observableFactory the factory function to obtain an Observable - * @return the Observable whose lifetime controls the lifetime of the dependent resource object - */ - def using[T, Resource <: Subscription](resourceFactory: () => Resource, observableFactory: Resource => Observable[T]): Observable[T] = { - class ResourceSubscription(val resource: Resource) extends rx.Subscription { - def unsubscribe = resource.unsubscribe - - def isUnsubscribed: Boolean = resource.isUnsubscribed - } - - toScalaObservable(rx.Observable.using[T, ResourceSubscription]( - () => new ResourceSubscription(resourceFactory()), - (s: ResourceSubscription) => observableFactory(s.resource).asJavaObservable - )) - } - - /** - * Mirror the one Observable in an Iterable of several Observables that first emits an item. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/amb.png"> - * - * @param sources an Iterable of Observable sources competing to react first - * @return an Observable that emits the same sequence of items as whichever of the source Observables - * first emitted an item - */ - def amb[T](sources: Observable[T]*): Observable[T] = { - toScalaObservable[T](rx.Observable.amb[T](sources.map(_.asJavaObservable).asJava)) - } - - /** - * Combines a list of source Observables by emitting an item that aggregates the latest values of each of - * the source Observables each time an item is received from any of the source Observables, where this - * aggregation is defined by a specified function. - * - * @tparam T the common base type of source values - * @tparam R the result type - * @param sources the list of source Observables - * @param combineFunction the aggregation function used to combine the items emitted by the source Observables - * @return an Observable that emits items that are the result of combining the items emitted by the source - * Observables by means of the given aggregation function - */ - def combineLatest[T, R](sources: Seq[Observable[T]])(combineFunction: Seq[T] => R): Observable[R] = { - val jSources = new java.util.ArrayList[rx.Observable[_ <: T]](sources.map(_.asJavaObservable).asJava) - val jCombineFunction = new rx.functions.FuncN[R] { - override def call(args: java.lang.Object*): R = combineFunction(args.map(_.asInstanceOf[T])) - } - toScalaObservable[R](rx.Observable.combineLatest[T, R](jSources, jCombineFunction)) - } -} - - - - - - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala deleted file mode 100644 index c47ee0a328..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observer.scala +++ /dev/null @@ -1,98 +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 scala.language.higherKinds - -/** - Provides a mechanism for receiving push-based notifications. -* -* After an Observer calls an [[rx.lang.scala.Observable]]'s `subscribe` method, the Observable -* calls the Observer's `onNext` method to provide notifications. A well-behaved Observable will -* call an Observer's `onCompleted` method exactly once or the Observer's `onError` method exactly once. -*/ -trait Observer[-T] { - - // Java calls XXX, Scala receives XXX - private [scala] val asJavaObserver: rx.Observer[_ >: T] = new rx.Observer[T] { - def onNext(value: T): Unit = Observer.this.onNext(value) - def onError(error: Throwable): Unit = Observer.this.onError(error) - def onCompleted(): Unit = Observer.this.onCompleted() - } - - /** - * Provides the Observer with new data. - * - * The [[rx.lang.scala.Observable]] calls this closure 0 or more times. - * - * The [[rx.lang.scala.Observable]] will not call this method again after it calls either `onCompleted` or `onError`. - */ - def onNext(value: T): Unit = {} - - /** - * Notifies the Observer that the [[rx.lang.scala.Observable]] has experienced an error condition. - * - * If the [[rx.lang.scala.Observable]] calls this method, it will not thereafter call `onNext` or `onCompleted`. - */ - def onError(error: Throwable): Unit= {} - - /** - * Notifies the Observer that the [[rx.lang.scala.Observable]] has finished sending push-based notifications. - * - * The [[rx.lang.scala.Observable]] will not call this method if it calls `onError`. - */ - def onCompleted(): Unit = {} - -} - -object Observer extends ObserverFactoryMethods[Observer] { - - /** - * Scala calls XXX; Java receives XXX. - */ - private [scala] def apply[T](observer: rx.Observer[T]) : Observer[T] = { - new Observer[T] { - override val asJavaObserver = observer - - override def onNext(value: T): Unit = asJavaObserver.onNext(value) - override def onError(error: Throwable): Unit = asJavaObserver.onError(error) - override def onCompleted(): Unit = asJavaObserver.onCompleted() - } - - } - - def apply[T](onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit): Observer[T] = { - val n = onNext; val e = onError; val c = onCompleted - // Java calls XXX; Scala receives XXX. - Observer(new rx.Observer[T] { - override def onNext(value: T): Unit = n(value) - override def onError(error: Throwable): Unit = e(error) - override def onCompleted(): Unit = c() - }) - } -} - - -private [scala] trait ObserverFactoryMethods[P[_] <: Observer[_]] { - - def apply[T](onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit): P[T] - - def apply[T]( ): P[T] = apply[T]((v:T)=>(), (e: Throwable)=>(), ()=>()) - def apply[T](onNext: T=>Unit ): P[T] = apply[T](onNext, (e: Throwable)=>(), ()=>()) - def apply[T](onNext: T=>Unit, onError: Throwable=>Unit ): P[T] = apply[T](onNext, onError, ()=>()) - def apply[T](onNext: T=>Unit, onCompleted: ()=>Unit): P[T] = apply[T](onNext, (e: Throwable)=>(), onCompleted) -} - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala deleted file mode 100644 index eee12bfc0a..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Scheduler.scala +++ /dev/null @@ -1,131 +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 scala.concurrent.duration.Duration -import rx.functions.Action0 -import rx.lang.scala.schedulers._ -import scala.concurrent.duration -import rx.lang.scala.JavaConversions._ - -/** - * Represents an object that schedules units of work. - */ -trait Scheduler { - - private [scala] val asJavaScheduler: rx.Scheduler - - /** - * Parallelism available to a Scheduler. - * - * This defaults to `Runtime.getRuntime().availableProcessors()` but can be overridden for use cases such as scheduling work on a computer cluster. - * - * @return the scheduler's available degree of parallelism. - */ - def parallelism: Int = asJavaScheduler.parallelism() - - /** - * @return the scheduler's notion of current absolute time in milliseconds. - */ - def now: Long = this.asJavaScheduler.now() - - /** - * Retrieve or create a new [[rx.lang.scala.Worker]] that represents serial execution of actions. - * <p> - * When work is completed it should be unsubscribed using [[rx.lang.scala.Worker unsubscribe]]. - * <p> - * Work on a [[rx.lang.scala.Worker]] is guaranteed to be sequential. - * - * @return Inner representing a serial queue of actions to be executed - */ - def createWorker: Worker = this.asJavaScheduler.createWorker() - -} - -object Worker { - def apply(worker: rx.Scheduler.Worker): Worker = new Worker { private[scala] val asJavaWorker = worker } -} - -trait Worker extends Subscription { - private [scala] val asJavaWorker: rx.Scheduler.Worker - - /** - * Schedules an Action for execution at some point in the future. - * - * @param action the Action to schedule - * @param delay time to wait before executing the action - * @return a subscription to be able to unsubscribe the action (unschedule it if not executed) - */ - def schedule(delay: Duration)(action: => Unit): Subscription = { - this.asJavaWorker.schedule( - new Action0 { - override def call(): Unit = action - }, - delay.length, - delay.unit) - } - - /** - * Schedules an Action for execution. - * - * @param action the Action to schedule - * @return a subscription to be able to unsubscribe the action (unschedule it if not executed) - */ - def schedule(action: => Unit): Subscription = { - this.asJavaWorker.schedule( - new Action0 { - override def call(): Unit = action - } - ) - } - - /** - * Schedules a cancelable action to be executed periodically. This default implementation schedules - * recursively and waits for actions to complete (instead of potentially executing long-running actions - * concurrently). Each scheduler that can do periodic scheduling in a better way should override this. - * - * @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 - * @return a subscription to be able to unsubscribe the action (unschedule it if not executed) - */ - def schedulePeriodically(initialDelay: Duration, period: Duration)(action: => Unit): Subscription = { - this.asJavaWorker.schedulePeriodically( - new Action0 { - override def call(): Unit = action - }, - initialDelay.toNanos, - period.toNanos, - duration.NANOSECONDS - ) - } - - /** - * @return the scheduler's notion of current absolute time in milliseconds. - */ - def now: Long = this.asJavaWorker.now() -} - - -private [scala] object Scheduler { - def apply(scheduler: rx.Scheduler): Scheduler = scheduler match { - case s: rx.schedulers.TestScheduler => new TestScheduler(s) - case s: rx.Scheduler => new Scheduler{ val asJavaScheduler = s } - } - -} - - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subject.scala deleted file mode 100644 index eb9efa02ac..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subject.scala +++ /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.lang.scala - -/** -* A Subject is an Observable and an Observer at the same time. -*/ -trait Subject[T] extends Observable[T] with Observer[T] { - private [scala] val asJavaSubject: rx.subjects.Subject[_ >: T, _<: T] - - val asJavaObservable: rx.Observable[_ <: T] = asJavaSubject - - override val asJavaObserver: rx.Observer[_ >: T] = asJavaSubject - override def onNext(value: T): Unit = { asJavaObserver.onNext(value)} - override def onError(error: Throwable): Unit = { asJavaObserver.onError(error) } - override def onCompleted() { asJavaObserver.onCompleted() } -} - -object Subject { - def apply[T](): Subject[T] = new rx.lang.scala.subjects.PublishSubject[T](rx.subjects.PublishSubject.create()) -} - - - - - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscriber.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscriber.scala deleted file mode 100644 index 06ae4d7fa5..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscriber.scala +++ /dev/null @@ -1,73 +0,0 @@ -package rx.lang.scala - -trait Subscriber[-T] extends Observer[T] with Subscription { - - self => - - private [scala] val asJavaSubscriber: rx.Subscriber[_ >: T] = new rx.Subscriber[T] { - def onNext(value: T): Unit = self.onNext(value) - def onError(error: Throwable): Unit = self.onError(error) - def onCompleted(): Unit = self.onCompleted() - } - - private [scala] override val asJavaObserver: rx.Observer[_ >: T] = asJavaSubscriber - private [scala] override val asJavaSubscription: rx.Subscription = asJavaSubscriber - - /** - * Used to register an unsubscribe callback. - */ - final def add(s: Subscription): Unit = { - asJavaSubscriber.add(s.asJavaSubscription) - } - - /** - * Register a callback to be run when Subscriber is unsubscribed - * - * @param u callback to run when unsubscribed - */ - final def add(u: => Unit): Unit = { - asJavaSubscriber.add(Subscription(u).asJavaSubscription) - } - - override final def unsubscribe(): Unit = { - asJavaSubscriber.unsubscribe() - } - - override final def isUnsubscribed: Boolean = { - asJavaSubscriber.isUnsubscribed() - } - -} - -object Subscriber extends ObserverFactoryMethods[Subscriber] { - - private[scala] def apply[T](subscriber: rx.Subscriber[T]): Subscriber[T] = new Subscriber[T] { - override val asJavaSubscriber = subscriber - override val asJavaObserver: rx.Observer[_ >: T] = asJavaSubscriber - override val asJavaSubscription: rx.Subscription = asJavaSubscriber - - override def onNext(value: T): Unit = asJavaSubscriber.onNext(value) - override def onError(error: Throwable): Unit = asJavaSubscriber.onError(error) - override def onCompleted(): Unit = asJavaSubscriber.onCompleted() - } - - def apply[T](onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit): Subscriber[T] = { - val n = onNext; val e = onError; val c = onCompleted - // Java calls XXX; Scala receives XXX. - Subscriber(new rx.Subscriber[T] { - override def onNext(value: T): Unit = n(value) - override def onError(error: Throwable): Unit = e(error) - override def onCompleted(): Unit = c() - }) - } - - def apply[T](subscriber: Subscriber[_], onNext: T => Unit, onError: Throwable => Unit, onCompleted: () => Unit): Subscriber[T] = { - val n = onNext; val e = onError; val c = onCompleted - // Java calls XXX; Scala receives XXX. - Subscriber(new rx.Subscriber[T](subscriber.asJavaSubscriber) { - override def onNext(value: T): Unit = n(value) - override def onError(error: Throwable): Unit = e(error) - override def onCompleted(): Unit = c() - }) - } -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala deleted file mode 100644 index 618e0abe8d..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Subscription.scala +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright 2014 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * 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 java.util.concurrent.atomic.AtomicBoolean - -/** - * Subscriptions are returned from all `Observable.subscribe` methods to allow unsubscribing. - * - * This interface is the equivalent of `IDisposable` in the .NET Rx implementation. - */ -trait Subscription { - - private [scala] val unsubscribed = new AtomicBoolean(false) - private [scala] val asJavaSubscription: rx.Subscription = new rx.Subscription { - override def unsubscribe() { unsubscribed.compareAndSet(false, true) } - override def isUnsubscribed(): Boolean = { unsubscribed.get() } - } - - - /** - * Call this method to stop receiving notifications on the Observer that was registered when - * this Subscription was received. - */ - def unsubscribe() = asJavaSubscription.unsubscribe() - - /** - * Checks if the subscription is unsubscribed. - */ - def isUnsubscribed = unsubscribed.get() - -} - -object Subscription { - - /** - * Creates an [[rx.lang.scala.Subscription]] from an [[rx.Subscription]]. ß - */ - private [scala] def apply(subscription: rx.Subscription): Subscription = { - subscription match { - case x: rx.subscriptions.BooleanSubscription => new rx.lang.scala.subscriptions.BooleanSubscription(x) - case x: rx.subscriptions.CompositeSubscription => new rx.lang.scala.subscriptions.CompositeSubscription(x) - case x: rx.subscriptions.MultipleAssignmentSubscription => new rx.lang.scala.subscriptions.MultipleAssignmentSubscription(x) - case x: rx.subscriptions.SerialSubscription => new rx.lang.scala.subscriptions.SerialSubscription(x) - case x: rx.Subscription => apply{ x.unsubscribe } - } - } - - /** - * Creates an [[rx.lang.scala.Subscription]] that invokes the specified action when unsubscribed. - */ - def apply(u: => Unit): Subscription = new Subscription() { - override val asJavaSubscription = new rx.Subscription { - override def unsubscribe() { if(unsubscribed.compareAndSet(false, true)) { u } } - override def isUnsubscribed(): Boolean = { unsubscribed.get() } - } - } - - /** - * Creates an empty [[rx.lang.scala.Subscription]]. - */ - def apply(): Subscription = Subscription {} - -} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala deleted file mode 100644 index 88fd326e2f..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/WithFilter.scala +++ /dev/null @@ -1,42 +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 ImplicitFunctionConversions.scalaBooleanFunction1ToRxBooleanFunc1 -import ImplicitFunctionConversions.scalaFunction1ToRxFunc1 - -// Cannot yet have inner class because of this error message: -// "implementation restriction: nested class is not allowed in value class. -// This restriction is planned to be removed in subsequent releases." -private[scala] class WithFilter[+T] (p: T => Boolean, asJava: rx.Observable[_ <: T]) { - - import ImplicitFunctionConversions._ - import JavaConversions._ - - def map[B](f: T => B): Observable[B] = { - toScalaObservable[B](asJava.filter(p).map[B](f)) - } - - def flatMap[B](f: T => Observable[B]): Observable[B] = { - toScalaObservable[B](asJava.filter(p).flatMap[B]((x: T) => f(x).asJavaObservable)) - } - - def withFilter(q: T => Boolean): Observable[T] = { - toScalaObservable[T](asJava.filter((x: T) => p(x) && q(x))) - } - - // there is no foreach here, that's only available on BlockingObservable -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala deleted file mode 100644 index 6dd2ab83ce..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/BlockingObservable.scala +++ /dev/null @@ -1,288 +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.observables - -import scala.collection.JavaConverters._ -import scala.concurrent.{Future, Promise} -import rx.lang.scala.ImplicitFunctionConversions._ -import rx.lang.scala.Observable -import rx.observables.{BlockingObservable => JBlockingObservable} - - -/** - * An Observable that provides blocking operators. - * - * You can obtain a BlockingObservable from an Observable using [[rx.lang.scala.Observable.toBlocking]] - */ -// constructor is private because users should use Observable.toBlocking -class BlockingObservable[+T] private[scala] (val o: Observable[T]) - extends AnyVal -{ - // This is def because "field definition is not allowed in value class" - private def asJava: JBlockingObservable[_ <: T] = o.asJavaObservable.toBlocking - /** - * 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 Observable#subscribe(Observer)}, but it blocks. Because it blocks it does - * not need the {@link Observer#onCompleted()} or {@link Observer#onError(Throwable)} methods. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/B.forEach.png"> - * - * @param f - * the {@link Action1} to invoke for every item emitted by the {@link Observable} - * @throws RuntimeException - * if an error occurs - */ - def foreach(f: T => Unit): Unit = { - asJava.forEach(f) - } - - def withFilter(p: T => Boolean): WithFilter[T] = { - new WithFilter[T](p, asJava) - } - - /** - * Returns the last item emitted by a specified [[Observable]], or - * throws `NoSuchElementException` if it emits no items. - * - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/B.last.png"> - * - * @return the last item emitted by the source [[Observable]] - * @throws NoSuchElementException - * if source contains no elements - * @see <a href="https://github.com/Netflix/RxJava/wiki/Blocking-Observable-Operators#last-and-lastordefault">RxJava Wiki: last()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/system.reactive.linq.observable.last.aspx">MSDN: Observable.Last</a> - */ - def last : T = { - asJava.last : T - } - - /** - * Returns an `Option` with the last item emitted by the source Observable, - * or `None` if the source Observable completes without emitting any items. - * - * @return an `Option` with the last item emitted by the source Observable, - * or `None` if the source Observable is empty - */ - def lastOption: Option[T] = { - o.lastOption.toBlocking.single - } - - /** - * Returns the last item emitted by the source Observable, or a default item - * if the source Observable completes without emitting any items. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/lastOrDefault.png"> - * - * @param default the default item to emit if the source Observable is empty. - * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything. - * @return the last item emitted by the source Observable, or a default item if the source Observable is empty - */ - def lastOrElse[U >: T](default: => U): U = { - lastOption getOrElse default - } - - /** - * Returns the first item emitted by a specified [[Observable]], or - * `NoSuchElementException` if source contains no elements. - * - * @return the first item emitted by the source [[Observable]] - * @throws NoSuchElementException - * if source contains no elements - * @see <a href="https://github.com/Netflix/RxJava/wiki/Blocking-Observable-Operators#first-and-firstordefault">RxJava Wiki: first()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229177.aspx">MSDN: Observable.First</a> - */ - def first : T = { - asJava.first : T - } - - /** - * Returns the first item emitted by a specified [[Observable]], or - * `NoSuchElementException` if source contains no elements. - * - * @return the first item emitted by the source [[Observable]] - * @throws NoSuchElementException - * if source contains no elements - * @see <a href="https://github.com/Netflix/RxJava/wiki/Blocking-Observable-Operators#first-and-firstordefault">RxJava Wiki: first()</a> - * @see <a href="http://msdn.microsoft.com/en-us/library/hh229177.aspx">MSDN: Observable.First</a> - * @see [[BlockingObservable.first]] - */ - def head : T = first - - /** - * Returns an `Option` with the very first item emitted by the source Observable, - * or `None` if the source Observable is empty. - * - * @return an `Option` with the very first item from the source, - * or `None` if the source Observable completes without emitting any item. - */ - def headOption: Option[T] = { - o.headOption.toBlocking.single - } - - /** - * Returns the very first item emitted by the source Observable, or a default value if the source Observable is empty. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/firstOrDefault.png"> - * - * @param default The default value to emit if the source Observable doesn't emit anything. - * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything. - * @return the very first item from the source, or a default value if the source Observable completes without emitting any item. - */ - def headOrElse[U >: T](default: => U): U = { - headOption getOrElse default - } - - /** - * Returns an {@link Iterable} that always returns the item most recently emitted by an {@link Observable}. - * <p> - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/B.mostRecent.png"> - * - * @param initialValue - * the initial value that will be yielded by the {@link Iterable} sequence if the {@link Observable} has not yet emitted an item - * @return an {@link Iterable} that on each iteration returns the item that the {@link Observable} has most recently emitted - */ - def mostRecent[U >: T](initialValue: U): Iterable[U] = { - val asJavaU = asJava.asInstanceOf[rx.observables.BlockingObservable[U]] - asJavaU.mostRecent(initialValue).asScala: Iterable[U] // useless ascription because of compiler bug - } - - /** - * Returns an {@link Iterable} that blocks until the {@link Observable} emits another item, - * then returns that item. - * <p> - * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/B.next.png"> - * - * @return an {@link Iterable} that blocks upon each iteration until the {@link Observable} emits a new item, whereupon the Iterable returns that item - */ - def next: Iterable[T] = { - asJava.next().asScala: Iterable[T] // useless ascription because of compiler bug - } - - /** - * If the source Observable completes after emitting a single item, return that item. If the source Observable - * emits more than one item or no items, notify of an `IllegalArgumentException` or `NoSuchElementException` respectively. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/single.png"> - * - * @return an Observable that emits the single item emitted by the source Observable - * @throws IllegalArgumentException if the source emits more than one item - * @throws NoSuchElementException if the source emits no items - */ - def single: T = { - asJava.single(): T // useless ascription because of compiler bug - } - - /** - * If the source Observable completes after emitting a single item, return an `Option` with that item; - * if the source Observable is empty, return `None`. If the source Observable emits more than one item, - * throw an `IllegalArgumentException`. - * - * @return an `Option` with the single item emitted by the source Observable, or - * `None` if the source Observable is empty - * @throws IllegalArgumentException if the source Observable emits more than one item - */ - def singleOption: Option[T] = { - o.singleOption.toBlocking.single - } - - /** - * If the source Observable completes after emitting a single item, return that item; - * if the source Observable is empty, return a default item. If the source Observable - * emits more than one item, throw an `IllegalArgumentException`. - * - * <img width="640" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/singleOrDefault.png"> - * - * @param default a default value to emit if the source Observable emits no item. - * This is a by-name parameter, so it is only evaluated if the source Observable doesn't emit anything. - * @return the single item emitted by the source Observable, or a default item if - * the source Observable is empty - * @throws IllegalArgumentException if the source Observable emits more than one item - */ - def singleOrElse[U >: T](default: => U): U = { - singleOption getOrElse default - } - - /** - * Returns an {@link Iterator} that iterates over all items emitted by this {@link Observable}. - */ - def toIterable: Iterable[T] = { - asJava.toIterable.asScala: Iterable[T] // useless ascription because of compiler bug - } - - /** - * Returns a {@link List} that contains all items emitted by this {@link Observable}. - */ - def toList: List[T] = { - asJava.toIterable.asScala.toList: List[T] // useless ascription because of compiler bug - } - - /** - * Returns an `Iterable` that returns the latest item emitted by this `BlockingObservable`, - * waiting if necessary for one to become available. - * - * If this `BlockingObservable` produces items faster than `Iterator.next` takes them, - * `onNext` events might be skipped, but `onError` or `onCompleted` events are not. - * - * Note also that an `onNext` directly followed by `onCompleted` might hide the `onNext` event. - * - * @return an `Iterable` that always returns the latest item emitted by this `BlockingObservable` - */ - def latest: Iterable[T] = { - asJava.latest.asScala: Iterable[T] // useless ascription because of compiler bug - } - - /** - * Returns a `Future` representing the single value emitted by this `BlockingObservable`. - * - * The returned `Future` will be completed with an `IllegalArgumentException` if the `BlockingObservable` - * emits more than one item. And it will be completed with an `NoSuchElementException` if the `BlockingObservable` - * is empty. Use `Observable.toSeq.toBlocking.toFuture` if you are not sure about the size of `BlockingObservable` - * and do not want to handle these `Exception`s. - * - * <img width="640" height="395" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/B.toFuture.png"> - * - * @return a `Future` that expects a single item to be emitted by this `BlockingObservable`. - */ - def toFuture: Future[T] = { - val p = Promise[T]() - o.single.subscribe(t => p.success(t), e => p.failure(e)) - p.future - } -} - -// Cannot yet have inner class because of this error message: -// "implementation restriction: nested class is not allowed in value class. -// This restriction is planned to be removed in subsequent releases." -private[observables] class WithFilter[+T] (p: T => Boolean, asJava: rx.observables.BlockingObservable[_ <: T]) { - import rx.lang.scala.ImplicitFunctionConversions._ - - // there's no map and flatMap here, they're only available on Observable - - def withFilter(q: T => Boolean) = new WithFilter[T]((x: T) => p(x) && q(x), asJava) - - def foreach(f: T => Unit): Unit = { - asJava.forEach((e: T) => { - if (p(e)) f(e) - }) - } - -} - - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala deleted file mode 100644 index bba27ecb22..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/ConnectableObservable.scala +++ /dev/null @@ -1,38 +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.observables - -import rx.lang.scala.{Observable, Subscription} -import rx.lang.scala.JavaConversions._ - -class ConnectableObservable[+T] private[scala](val asJavaObservable: rx.observables.ConnectableObservable[_ <: T]) - extends Observable[T] { - - /** - * Call a ConnectableObservable's connect method to instruct it to begin emitting the - * items from its underlying [[rx.lang.scala.Observable]] to its [[rx.lang.scala.Observer]]s. - */ - def connect: Subscription = toScalaSubscription(asJavaObservable.connect()) - - /** - * Returns an observable sequence that stays connected to the source as long - * as there is at least one subscription to the observable sequence. - * - * @return a [[rx.lang.scala.Observable]] - */ - def refCount: Observable[T] = toScalaObservable[T](asJavaObservable.refCount()) -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala deleted file mode 100644 index 12f830dc9a..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/observables/package.scala +++ /dev/null @@ -1,27 +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 - -/** - * Contains special Observables. - * - * In Scala, this package only contains [[rx.lang.scala.observables.BlockingObservable]]. - * In the corresponding Java package `rx.observables`, there is also a - * `GroupedObservable` and a `ConnectableObservable`, but these are not needed - * in Scala, because we use a pair `(key, observable)` instead of `GroupedObservable` - * and a pair `(startFunction, observable)` instead of `ConnectableObservable`. - */ -package object observables {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala deleted file mode 100644 index aa6cd4339e..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/package.scala +++ /dev/null @@ -1,35 +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 - -/** - * This package contains all classes that RxScala users need. - * - * It basically mirrors the structure of package `rx`, but some changes were made to make it more Scala-idiomatic. - */ -package object scala { - - /** - * Placeholder for extension methods into Observable[T] from other types - */ - implicit class ObservableExtensions[T](val source: Iterable[T]) extends AnyVal { - def toObservable: Observable[T] = { Observable.from(source) } - def toObservable(scheduler: Scheduler): Observable[T] = { Observable.from(source, scheduler) } - } - - - -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/ComputationScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/ComputationScheduler.scala deleted file mode 100644 index 80621db933..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/ComputationScheduler.scala +++ /dev/null @@ -1,22 +0,0 @@ -package rx.lang.scala.schedulers - -import rx.lang.scala.Scheduler - - -object ComputationScheduler { - /** - * [[rx.lang.scala.Scheduler]] intended for computational work. - * <p> - * This can be used for event-loops, processing callbacks and other computational work. - * <p> - * Do not perform IO-bound work on this scheduler. Use [[rx.lang.scala.schedulers.IOScheduler]] instead. - * - * @return [[rx.lang.scala.Scheduler]] for computation-bound work. - */ - def apply(): ComputationScheduler = { - new ComputationScheduler(rx.schedulers.Schedulers.computation()) - } -} - -class ComputationScheduler private[scala] (val asJavaScheduler: rx.Scheduler) - extends Scheduler {} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/IOScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/IOScheduler.scala deleted file mode 100644 index 339d82fd6a..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/IOScheduler.scala +++ /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.lang.scala.schedulers - -import rx.lang.scala.Scheduler - -object IOScheduler { - /** - * [[rx.lang.scala.Scheduler]] intended for IO-bound work. - * <p> - * The implementation is backed by an `Executor` thread-pool that will grow as needed. - * <p> - * This can be used for asynchronously performing blocking IO. - * <p> - * Do not perform computational work on this scheduler. Use [[rx.lang.scala.schedulers.ComputationScheduler]] instead. - * - * @return [[rx.lang.scala.Scheduler]] for IO-bound work - */ - def apply(): IOScheduler = { - new IOScheduler(rx.schedulers.Schedulers.io) - } -} - -class IOScheduler private[scala] (val asJavaScheduler: rx.Scheduler) - extends Scheduler {} - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/ImmediateScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/ImmediateScheduler.scala deleted file mode 100644 index cc22456afa..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/ImmediateScheduler.scala +++ /dev/null @@ -1,33 +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.schedulers - -import rx.lang.scala.Scheduler - -object ImmediateScheduler { - - /** - * Returns a [[rx.lang.scala.Scheduler]] that executes work immediately on the current thread. - */ - def apply(): ImmediateScheduler = { - new ImmediateScheduler(rx.schedulers.Schedulers.immediate()) - } -} - -class ImmediateScheduler private[scala] (val asJavaScheduler: rx.Scheduler) - extends Scheduler {} - - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/NewThreadScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/NewThreadScheduler.scala deleted file mode 100644 index 09b0f30c08..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/NewThreadScheduler.scala +++ /dev/null @@ -1,30 +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.schedulers - -import rx.lang.scala.Scheduler - -object NewThreadScheduler { - - /** - * Returns a [[rx.lang.scala.Scheduler]] that creates a new `java.lang.Thread` for each unit of work. - */ - def apply(): NewThreadScheduler = { - new NewThreadScheduler(rx.schedulers.Schedulers.newThread()) - } -} - -class NewThreadScheduler private[scala] (val asJavaScheduler: rx.Scheduler) extends Scheduler {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/TestScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/TestScheduler.scala deleted file mode 100644 index c8ddb76673..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/TestScheduler.scala +++ /dev/null @@ -1,81 +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.schedulers - -import scala.concurrent.duration.Duration -import rx.lang.scala.Scheduler -import rx.schedulers - -/** - * Provides constructors for `TestScheduler`. - */ -object TestScheduler { - def apply(): TestScheduler = { - new TestScheduler(new schedulers.TestScheduler()) - } -} - -/** - * Scheduler with artificial time, useful for testing. - * - * For example, you could test the `Observable.interval` operation using a `TestScheduler` as follows: - * - * {{{ - * @Test def testInterval() { - * import org.mockito.Matchers._ - * import org.mockito.Mockito._ - * - * val scheduler = TestScheduler() - * val observer = mock(classOf[rx.Observer[Long]]) - * - * val o = Observable.interval(1 second, scheduler) - * val sub = o.subscribe(observer) - * - * verify(observer, never).onNext(0L) - * verify(observer, never).onCompleted() - * verify(observer, never).onError(any(classOf[Throwable])) - * - * scheduler.advanceTimeTo(2 seconds) - * - * val inOrdr = inOrder(observer); - * inOrdr.verify(observer, times(1)).onNext(0L) - * inOrdr.verify(observer, times(1)).onNext(1L) - * inOrdr.verify(observer, never).onNext(2L) - * verify(observer, never).onCompleted() - * verify(observer, never).onError(any(classOf[Throwable])) - * - * sub.unsubscribe(); - * scheduler.advanceTimeTo(4 seconds) - * verify(observer, never).onNext(2L) - * verify(observer, times(1)).onCompleted() - * verify(observer, never).onError(any(classOf[Throwable])) - * } - * }}} - */ -class TestScheduler private[scala] (val asJavaScheduler: rx.schedulers.TestScheduler) extends Scheduler { - - def advanceTimeBy(time: Duration) { - asJavaScheduler.advanceTimeBy(time.length, time.unit) - } - - def advanceTimeTo(time: Duration) { - asJavaScheduler.advanceTimeTo(time.length, time.unit) - } - - def triggerActions() { - asJavaScheduler.triggerActions() - } -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/TrampolineScheduler.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/TrampolineScheduler.scala deleted file mode 100644 index e2cdc49e7d..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/schedulers/TrampolineScheduler.scala +++ /dev/null @@ -1,15 +0,0 @@ -package rx.lang.scala.schedulers - -import rx.lang.scala.Scheduler - -object TrampolineScheduler { - /** - * [[rx.lang.scala.Scheduler]] that queues work on the current thread to be executed after the current work completes. - */ - def apply(): TrampolineScheduler = { - new TrampolineScheduler(rx.schedulers.Schedulers.trampoline()) - } -} - -class TrampolineScheduler private[scala] (val asJavaScheduler: rx.Scheduler) - extends Scheduler {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala deleted file mode 100644 index 44cb6adb79..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/AsyncSubject.scala +++ /dev/null @@ -1,26 +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.subjects - -import rx.lang.scala.Subject - -object AsyncSubject { - def apply[T](): AsyncSubject[T] = { - new AsyncSubject[T](rx.subjects.AsyncSubject.create()) - } -} - -class AsyncSubject[T] private[scala] (val asJavaSubject: rx.subjects.AsyncSubject[T]) extends Subject[T] {} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala deleted file mode 100644 index 64e0ac4671..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/BehaviorSubject.scala +++ /dev/null @@ -1,30 +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.subjects - -import rx.lang.scala.Subject - -object BehaviorSubject { - def apply[T](value: T): BehaviorSubject[T] = { - new BehaviorSubject[T](rx.subjects.BehaviorSubject.create(value)) - } -} - -class BehaviorSubject[T] private[scala] (val asJavaSubject: rx.subjects.BehaviorSubject[T]) extends Subject[T] {} - - - - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala deleted file mode 100644 index 577f6693ea..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/PublishSubject.scala +++ /dev/null @@ -1,24 +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.subjects - -import rx.lang.scala.Subject - -object PublishSubject { - def apply[T](): PublishSubject[T] = new PublishSubject[T](rx.subjects.PublishSubject.create[T]()) -} - -private [scala] class PublishSubject[T] private [scala] (val asJavaSubject: rx.subjects.PublishSubject[T]) extends Subject[T] {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala deleted file mode 100644 index 3f64bf1e7d..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/ReplaySubject.scala +++ /dev/null @@ -1,30 +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.subjects - -import rx.lang.scala.Subject - -object ReplaySubject { - def apply[T](): ReplaySubject[T] = { - new ReplaySubject[T](rx.subjects.ReplaySubject.create()) - } -} - -class ReplaySubject[T] private[scala] (val asJavaSubject: rx.subjects.ReplaySubject[T]) extends Subject[T] { -} - - - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala deleted file mode 100644 index 7566d5fad8..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subjects/package.scala +++ /dev/null @@ -1,21 +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 - -/** - * Subjects are Observers and Observables at the same time. - */ -package object subjects {} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala deleted file mode 100644 index 5f07497ca3..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/BooleanSubscription.scala +++ /dev/null @@ -1,42 +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.subscriptions - -import rx.lang.scala._ - -private [scala] object BooleanSubscription { - def apply(): BooleanSubscription = new BooleanSubscription(new rx.subscriptions.BooleanSubscription()) -} - -/** - * Represents a [[rx.lang.scala.Subscription]] that can be checked for status. - */ -private [scala] class BooleanSubscription private[scala] (boolean: rx.subscriptions.BooleanSubscription) - extends Subscription { - - override val asJavaSubscription: rx.subscriptions.BooleanSubscription = boolean -} - -/* -new rx.subscriptions.BooleanSubscription() { - override def unsubscribe(): Unit = { - if(unsubscribed.compareAndSet(false, true)) { - if(!boolean.isUnsubscribed) { boolean.unsubscribe() } - } - } - override def isUnsubscribed(): Boolean = unsubscribed.get() || boolean.isUnsubscribed - } - */ \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala deleted file mode 100644 index dcd0ca5c39..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/CompositeSubscription.scala +++ /dev/null @@ -1,75 +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.subscriptions - -import rx.lang.scala._ - -object CompositeSubscription { - - /** - * Creates a [[rx.lang.scala.subscriptions.CompositeSubscription]] from a group of [[rx.lang.scala.Subscription]]. - */ - def apply(subscriptions: Subscription*): CompositeSubscription = { - new CompositeSubscription(new rx.subscriptions.CompositeSubscription(subscriptions.map(_.asJavaSubscription).toArray : _*)) - } - - /** - * Creates a [[rx.lang.scala.subscriptions.CompositeSubscription]]. - */ - def apply(): CompositeSubscription = { - new CompositeSubscription(new rx.subscriptions.CompositeSubscription()) - } - - /** - * Creates a [[rx.lang.scala.subscriptions.CompositeSubscription]]. - */ - private [scala] def apply(subscription: rx.subscriptions.CompositeSubscription): CompositeSubscription = { - new CompositeSubscription(subscription) - } -} - -/** - * Represents a group of [[rx.lang.scala.Subscription]] that are disposed together. - */ -class CompositeSubscription private[scala] (override val asJavaSubscription: rx.subscriptions.CompositeSubscription) extends Subscription -{ - //override def asJavaSubscription = subscription - - /** - * Adds a subscription to the group, - * or unsubscribes immediately is the [[rx.lang.scala.subscriptions.CompositeSubscription]] is unsubscribed. - * @param subscription the subscription to be added. - * @return the [[rx.lang.scala.subscriptions.CompositeSubscription]] itself. - */ - def +=(subscription: Subscription): this.type = { - asJavaSubscription.add(subscription.asJavaSubscription) - this - } - - /** - * Removes and unsubscribes a subscription to the group, - * @param subscription the subscription be removed. - * @return the [[rx.lang.scala.subscriptions.CompositeSubscription]] itself. - */ - def -=(subscription: Subscription): this.type = { - asJavaSubscription.remove(subscription.asJavaSubscription) - this - } - - override def unsubscribe(): Unit = asJavaSubscription.unsubscribe() - override def isUnsubscribed: Boolean = asJavaSubscription.isUnsubscribed - -} diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala deleted file mode 100644 index 1cbb0f8d6d..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/MultiAssignmentSubscription.scala +++ /dev/null @@ -1,67 +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.subscriptions - -import rx.lang.scala._ - -object MultipleAssignmentSubscription { - - /** - * Creates a [[rx.lang.scala.subscriptions.MultipleAssignmentSubscription]] that invokes the specified action when unsubscribed. - */ - def apply(subscription: => Unit): MultipleAssignmentSubscription = { - val m = MultipleAssignmentSubscription() - m.subscription = Subscription{ subscription } - m - } - - /** - * Creates a [[rx.lang.scala.subscriptions.MultipleAssignmentSubscription]]. - */ - def apply(): MultipleAssignmentSubscription = { - new MultipleAssignmentSubscription(new rx.subscriptions.MultipleAssignmentSubscription()) - } -} - - - -/** - * Represents a [[rx.lang.scala.Subscription]] whose underlying subscription can be swapped for another subscription. - */ -class MultipleAssignmentSubscription private[scala] (override val asJavaSubscription: rx.subscriptions.MultipleAssignmentSubscription) extends Subscription { - - //override def asJavaSubscription = s - /** - * Gets the underlying subscription. - */ - def subscription: Subscription = Subscription(asJavaSubscription.get) - - /** - * Gets the underlying subscription - * @param that the new subscription - * @return the [[rx.lang.scala.subscriptions.MultipleAssignmentSubscription]] itself. - */ - def subscription_=(that: Subscription): this.type = { - asJavaSubscription.set(that.asJavaSubscription) - this - } - - override def unsubscribe(): Unit = asJavaSubscription.unsubscribe() - override def isUnsubscribed: Boolean = asJavaSubscription.isUnsubscribed - -} - - diff --git a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala b/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala deleted file mode 100644 index 77b52e3b5f..0000000000 --- a/language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/subscriptions/SerialSubscription.scala +++ /dev/null @@ -1,50 +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.subscriptions - -import rx.lang.scala._ - -object SerialSubscription { - - /** - * Creates a [[rx.lang.scala.subscriptions.SerialSubscription]]. - */ - def apply(): SerialSubscription = new SerialSubscription(new rx.subscriptions.SerialSubscription()) - - /** - * Creates a [[rx.lang.scala.subscriptions.SerialSubscription]] that invokes the specified action when unsubscribed. - */ - def apply(unsubscribe: => Unit): SerialSubscription = { - SerialSubscription().subscription = Subscription(unsubscribe) - } -} - -/** - * Represents a [[rx.lang.scala.Subscription]] that can be checked for status. - */ -class SerialSubscription private[scala] (override val asJavaSubscription: rx.subscriptions.SerialSubscription) extends Subscription { - - override def unsubscribe(): Unit = asJavaSubscription.unsubscribe() - override def isUnsubscribed: Boolean = asJavaSubscription.isUnsubscribed - - def subscription_=(value: Subscription): this.type = { - asJavaSubscription.set(value.asJavaSubscription) - this - } - def subscription: Subscription = Subscription(asJavaSubscription.get) - -} - diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala deleted file mode 100644 index 7b85189dce..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala +++ /dev/null @@ -1,486 +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 java.util.Calendar - -import scala.collection.SortedMap -import scala.reflect.runtime.universe -import scala.reflect.runtime.universe.Symbol -import scala.reflect.runtime.universe.Type -import scala.reflect.runtime.universe.typeOf - -import org.junit.Ignore -import org.junit.Test -import org.scalatest.junit.JUnitSuite - -/** - * These tests can be used to check if all methods of the Java Observable have a corresponding - * method in the Scala Observable. - * - * These tests don't contain any assertions, so they will always succeed, but they print their - * results to stdout. - */ -class CompletenessTest extends JUnitSuite { - - // some frequently used comments: - val unnecessary = "[considered unnecessary in Scala land]" - val deprecated = "[deprecated in RxJava]" - val averageProblem = "[We can't have a general average method because Scala's `Numeric` does not have " + - "scalar multiplication (we would need to calculate `(1.0/numberOfElements)*sum`). " + - "You can use `fold` instead to accumulate `sum` and `numberOfElements` and divide at the end.]" - val commentForFirstWithPredicate = "[use `.filter(condition).first`]" - val fromFuture = "[TODO: Decide how Scala Futures should relate to Observables. Should there be a " + - "common base interface for Future and Observable? And should Futures also have an unsubscribe method?]" - val commentForTakeLastBuffer = "[use `takeRight(...).toSeq`]" - - /** - * Maps each method from the Java Observable to its corresponding method in the Scala Observable - */ - val correspondence = defaultMethodCorrespondence ++ correspondenceChanges // ++ overrides LHS with RHS - - /** - * Creates default method correspondence mappings, assuming that Scala methods have the same - * name and the same argument types as in Java - */ - def defaultMethodCorrespondence: Map[String, String] = { - val allMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.Observable[_]]) - val tuples = for (javaM <- allMethods) yield (javaM, javaMethodSignatureToScala(javaM)) - tuples.toMap - } - - /** - * Manually added mappings from Java Observable methods to Scala Observable methods - */ - def correspondenceChanges = Map( - // manually added entries for Java instance methods - "aggregate(Func2[T, T, T])" -> "reduce((U, U) => U)", - "aggregate(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", - "all(Func1[_ >: T, Boolean])" -> "forall(T => Boolean)", - "asObservable()" -> unnecessary, - "buffer(Int)" -> "tumblingBuffer(Int)", - "buffer(Int, Int)" -> "slidingBuffer(Int, Int)", - "buffer(Long, TimeUnit)" -> "tumblingBuffer(Duration)", - "buffer(Long, TimeUnit, Int)" -> "tumblingBuffer(Duration, Int)", - "buffer(Long, TimeUnit, Int, Scheduler)" -> "tumblingBuffer(Duration, Int, Scheduler)", - "buffer(Long, TimeUnit, Scheduler)" -> "tumblingBuffer(Duration, Scheduler)", - "buffer(Long, Long, TimeUnit)" -> "slidingBuffer(Duration, Duration)", - "buffer(Long, Long, TimeUnit, Scheduler)" -> "slidingBuffer(Duration, Duration, Scheduler)", - "buffer(Func0[_ <: Observable[_ <: TClosing]])" -> "tumblingBuffer(=> Observable[Any])", - "buffer(Observable[B])" -> "tumblingBuffer(=> Observable[Any])", - "buffer(Observable[B], Int)" -> "tumblingBuffer(Observable[Any], Int)", - "buffer(Observable[_ <: TOpening], Func1[_ >: TOpening, _ <: Observable[_ <: TClosing]])" -> "slidingBuffer(Observable[Opening])(Opening => Observable[Any])", - "cast(Class[R])" -> "[RxJava needs this one because `rx.Observable` is invariant. `Observable` in RxScala is covariant and does not need this operator.]", - "collect(R, Action2[R, _ >: T])" -> "foldLeft(R)((R, T) => R)", - "contains(Any)" -> "contains(U)", - "count()" -> "length", - "debounce(Func1[_ >: T, _ <: Observable[U]])" -> "debounce(T => Observable[Any])", - "defaultIfEmpty(T)" -> "orElse(=> U)", - "delay(Func0[_ <: Observable[U]], Func1[_ >: T, _ <: Observable[V]])" -> "delay(() => Observable[Any], T => Observable[Any])", - "delay(Func1[_ >: T, _ <: Observable[U]])" -> "delay(T => Observable[Any])", - "dematerialize()" -> "dematerialize(<:<[Observable[T], Observable[Notification[U]]])", - "doOnEach(Action1[Notification[_ >: T]])" -> "[use `doOnEach(T => Unit, Throwable => Unit, () => Unit)`]", - "elementAtOrDefault(Int, T)" -> "elementAtOrDefault(Int, U)", - "first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate, - "firstOrDefault(T)" -> "firstOrElse(=> U)", - "firstOrDefault(T, Func1[_ >: T, Boolean])" -> "[use `.filter(condition).firstOrElse(default)`]", - "forEach(Action1[_ >: T])" -> "foreach(T => Unit)", - "forEach(Action1[_ >: T], Action1[Throwable])" -> "foreach(T => Unit, Throwable => Unit)", - "forEach(Action1[_ >: T], Action1[Throwable], Action0)" -> "foreach(T => Unit, Throwable => Unit, () => Unit)", - "groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> "[use `groupBy` and `map`]", - "groupByUntil(Func1[_ >: T, _ <: TKey], Func1[_ >: GroupedObservable[TKey, T], _ <: Observable[_ <: TDuration]])" -> "groupByUntil(T => K)((K, Observable[T]) => Observable[Any])", - "groupByUntil(Func1[_ >: T, _ <: TKey], Func1[_ >: T, _ <: TValue], Func1[_ >: GroupedObservable[TKey, TValue], _ <: Observable[_ <: TDuration]])" -> "groupByUntil(T => K, T => V)((K, Observable[V]) => Observable[Any])", - "groupJoin(Observable[T2], Func1[_ >: T, _ <: Observable[D1]], Func1[_ >: T2, _ <: Observable[D2]], Func2[_ >: T, _ >: Observable[T2], _ <: R])" -> "groupJoin(Observable[S])(T => Observable[Any], S => Observable[Any], (T, Observable[S]) => R)", - "ignoreElements()" -> "[use `filter(_ => false)`]", - "join(Observable[TRight], Func1[T, Observable[TLeftDuration]], Func1[TRight, Observable[TRightDuration]], Func2[T, TRight, R])" -> "join(Observable[S])(T => Observable[Any], S => Observable[Any], (T, S) => R)", - "last(Func1[_ >: T, Boolean])" -> "[use `filter(predicate).last`]", - "lastOrDefault(T)" -> "lastOrElse(=> U)", - "lastOrDefault(T, Func1[_ >: T, Boolean])" -> "[use `filter(predicate).lastOrElse(default)`]", - "lift(Operator[_ <: R, _ >: T])" -> "lift(Subscriber[R] => Subscriber[T])", - "limit(Int)" -> "take(Int)", - "mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> "[combine `zipWithIndex` with `map` or with a for comprehension]", - "mergeMap(Func1[_ >: T, _ <: Observable[_ <: R]])" -> "flatMap(T => Observable[R])", - "mergeMap(Func1[_ >: T, _ <: Observable[_ <: R]], Func1[_ >: Throwable, _ <: Observable[_ <: R]], Func0[_ <: Observable[_ <: R]])" -> "flatMap(T => Observable[R], Throwable => Observable[R], () => Observable[R])", - "mergeMap(Func1[_ >: T, _ <: Observable[_ <: U]], Func2[_ >: T, _ >: U, _ <: R])" -> "flatMapWith(T => Observable[U])((T, U) => R)", - "mergeMapIterable(Func1[_ >: T, _ <: Iterable[_ <: R]])" -> "flatMapIterable(T => Iterable[R])", - "mergeMapIterable(Func1[_ >: T, _ <: Iterable[_ <: U]], Func2[_ >: T, _ >: U, _ <: R])" -> "flatMapIterableWith(T => Iterable[U])((T, U) => R)", - "multicast(Subject[_ >: T, _ <: R])" -> "multicast(Subject[R])", - "multicast(Func0[_ <: Subject[_ >: T, _ <: TIntermediate]], Func1[_ >: Observable[TIntermediate], _ <: Observable[TResult]])" -> "multicast(() => Subject[R])(Observable[R] => Observable[U])", - "ofType(Class[R])" -> "[use `filter(_.isInstanceOf[Class])", - "onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> "onErrorResumeNext(Throwable => Observable[U])", - "onErrorResumeNext(Observable[_ <: T])" -> "onErrorResumeNext(Observable[U])", - "onErrorReturn(Func1[Throwable, _ <: T])" -> "onErrorReturn(Throwable => U)", - "onExceptionResumeNext(Observable[_ <: T])" -> "onExceptionResumeNext(Observable[U])", - "onErrorFlatMap(Func1[OnErrorThrowable, _ <: Observable[_ <: T]])" -> "onErrorFlatMap((Throwable, Option[Any]) => Observable[U])", - "parallel(Func1[Observable[T], Observable[R]])" -> "parallel(Observable[T] => Observable[R])", - "parallel(Func1[Observable[T], Observable[R]], Scheduler)" -> "parallel(Observable[T] => Observable[R], Scheduler)", - "publish(T)" -> "publish(U)", - "publish(Func1[_ >: Observable[T], _ <: Observable[R]])" -> "publish(Observable[U] => Observable[R])", - "publish(Func1[_ >: Observable[T], _ <: Observable[R]], T)" -> "publish(Observable[U] => Observable[R], U)", - "publishLast(Func1[_ >: Observable[T], _ <: Observable[R]])" -> "publishLast(Observable[T] => Observable[R])", - "reduce(Func2[T, T, T])" -> "reduce((U, U) => U)", - "reduce(R, Func2[R, _ >: T, R])" -> "foldLeft(R)((R, T) => R)", - "replay(Func1[_ >: Observable[T], _ <: Observable[R]])" -> "replay(Observable[U] => Observable[R])", - "replay(Func1[_ >: Observable[T], _ <: Observable[R]], Int)" -> "replay(Observable[U] => Observable[R], Int)", - "replay(Func1[_ >: Observable[T], _ <: Observable[R]], Int, Long, TimeUnit)" -> "replay(Observable[U] => Observable[R], Int, Duration)", - "replay(Func1[_ >: Observable[T], _ <: Observable[R]], Int, Long, TimeUnit, Scheduler)" -> "replay(Observable[U] => Observable[R], Int, Duration, Scheduler)", - "replay(Func1[_ >: Observable[T], _ <: Observable[R]], Int, Scheduler)" -> "replay(Observable[U] => Observable[R], Int, Scheduler)", - "replay(Func1[_ >: Observable[T], _ <: Observable[R]], Long, TimeUnit)" -> "replay(Observable[U] => Observable[R], Duration)", - "replay(Func1[_ >: Observable[T], _ <: Observable[R]], Long, TimeUnit, Scheduler)" -> "replay(Observable[U] => Observable[R], Duration, Scheduler)", - "replay(Func1[_ >: Observable[T], _ <: Observable[R]], Scheduler)" -> "replay(Observable[U] => Observable[R], Scheduler)", - "retry(Func2[Integer, Throwable, Boolean])" -> "retry((Int, Throwable) => Boolean)", - "sample(Observable[U])" -> "sample(Observable[Any])", - "scan(Func2[T, T, T])" -> unnecessary, - "scan(R, Func2[R, _ >: T, R])" -> "scan(R)((R, T) => R)", - "single(Func1[_ >: T, Boolean])" -> "[use `filter(predicate).single`]", - "singleOrDefault(T)" -> "singleOrElse(=> U)", - "singleOrDefault(T, Func1[_ >: T, Boolean])" -> "[use `filter(predicate).singleOrElse(default)`]", - "skip(Int)" -> "drop(Int)", - "skip(Long, TimeUnit)" -> "drop(Duration)", - "skip(Long, TimeUnit, Scheduler)" -> "drop(Duration, Scheduler)", - "skipWhile(Func1[_ >: T, Boolean])" -> "dropWhile(T => Boolean)", - "skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary, - "skipUntil(Observable[U])" -> "dropUntil(Observable[E])", - "startWith(T)" -> "[use `item +: o`]", - "startWith(Array[T])" -> "[use `Observable.items(items) ++ o`]", - "startWith(Array[T], Scheduler)" -> "[use `Observable.items(items).subscribeOn(scheduler) ++ o`]", - "startWith(Iterable[T])" -> "[use `Observable.from(iterable) ++ o`]", - "startWith(Iterable[T], Scheduler)" -> "[use `Observable.from(iterable).subscribeOn(scheduler) ++ o`]", - "startWith(Observable[T])" -> "[use `++`]", - "skipLast(Int)" -> "dropRight(Int)", - "skipLast(Long, TimeUnit)" -> "dropRight(Duration)", - "skipLast(Long, TimeUnit, Scheduler)" -> "dropRight(Duration, Scheduler)", - "subscribe()" -> "subscribe()", - "takeFirst(Func1[_ >: T, Boolean])" -> "[use `filter(condition).take(1)`]", - "takeLast(Int)" -> "takeRight(Int)", - "takeLast(Long, TimeUnit)" -> "takeRight(Duration)", - "takeLast(Long, TimeUnit, Scheduler)" -> "takeRight(Duration, Scheduler)", - "takeLast(Int, Long, TimeUnit)" -> "takeRight(Int, Duration)", - "takeLast(Int, Long, TimeUnit, Scheduler)" -> "takeRight(Int, Duration, Scheduler)", - "takeLastBuffer(Int)" -> commentForTakeLastBuffer, - "takeLastBuffer(Int, Long, TimeUnit)" -> commentForTakeLastBuffer, - "takeLastBuffer(Int, Long, TimeUnit, Scheduler)" -> commentForTakeLastBuffer, - "takeLastBuffer(Long, TimeUnit)" -> commentForTakeLastBuffer, - "takeLastBuffer(Long, TimeUnit, Scheduler)" -> commentForTakeLastBuffer, - "takeWhileWithIndex(Func2[_ >: T, _ >: Integer, Boolean])" -> "[use `.zipWithIndex.takeWhile{case (elem, index) => condition}.map(_._1)`]", - "timeout(Func0[_ <: Observable[U]], Func1[_ >: T, _ <: Observable[V]], Observable[_ <: T])" -> "timeout(() => Observable[U], T => Observable[V], Observable[O])", - "timeout(Func1[_ >: T, _ <: Observable[V]], Observable[_ <: T])" -> "timeout(() => Observable[U], T => Observable[V])", - "timeout(Long, TimeUnit, Observable[_ <: T])" -> "timeout(Duration, Observable[U])", - "timeout(Long, TimeUnit, Observable[_ <: T], Scheduler)" -> "timeout(Duration, Observable[U], Scheduler)", - "timer(Long, Long, TimeUnit)" -> "timer(Duration, Duration)", - "timer(Long, Long, TimeUnit, Scheduler)" -> "timer(Duration, Duration, Scheduler)", - "toList()" -> "toSeq", - "toMultimap(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: V], Func0[_ <: Map[K, Collection[V]]])" -> "toMultimap(T => K, T => V, () => M)", - "toMultimap(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: V], Func0[_ <: Map[K, Collection[V]]], Func1[_ >: K, _ <: Collection[V]])" -> "toMultimap(T => K, T => V, () => M, K => B)", - "toSortedList()" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)`]", - "toSortedList(Func2[_ >: T, _ >: T, Integer])" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))`]", - "window(Int)" -> "tumbling(Int)", - "window(Int, Int)" -> "sliding(Int, Int)", - "window(Long, TimeUnit)" -> "tumbling(Duration)", - "window(Long, TimeUnit, Int)" -> "tumbling(Duration, Int)", - "window(Long, TimeUnit, Int, Scheduler)" -> "tumbling(Duration, Int, Scheduler)", - "window(Long, TimeUnit, Scheduler)" -> "tumbling(Duration, Scheduler)", - "window(Observable[U])" -> "tumbling(=> Observable[Any])", - "window(Func0[_ <: Observable[_ <: TClosing]])" -> "tumbling(=> Observable[Any])", - "window(Observable[_ <: TOpening], Func1[_ >: TOpening, _ <: Observable[_ <: TClosing]])" -> "sliding(Observable[Opening])(Opening => Observable[Any])", - "window(Long, Long, TimeUnit)" -> "sliding(Duration, Duration)", - "window(Long, Long, TimeUnit, Scheduler)" -> "sliding(Duration, Duration, Scheduler)", - - // manually added entries for Java static methods - "amb(Iterable[_ <: Observable[_ <: T]])" -> "amb(Observable[T]*)", - "average(Observable[Integer])" -> averageProblem, - "averageDoubles(Observable[Double])" -> averageProblem, - "averageFloats(Observable[Float])" -> averageProblem, - "averageLongs(Observable[Long])" -> averageProblem, - "create(OnSubscribeFunc[T])" -> "create(Observer[T] => Subscription)", - "create(OnSubscribe[T])" -> "apply(Subscriber[T] => Unit)", - "combineLatest(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "combineLatest(Observable[U])", - "combineLatest(List[_ <: Observable[_ <: T]], FuncN[_ <: R])" -> "combineLatest(Seq[Observable[T]])(Seq[T] => R)", - "concat(Observable[_ <: Observable[_ <: T]])" -> "concat(<:<[Observable[T], Observable[Observable[U]]])", - "defer(Func0[_ <: Observable[_ <: T]])" -> "defer(=> Observable[T])", - "from(Array[T])" -> "items(T*)", - "from(<repeated...>[T])" -> "items(T*)", - "from(Array[T], Scheduler)" -> "from(Iterable[T], Scheduler)", - "from(Iterable[_ <: T])" -> "from(Iterable[T])", - "from(Future[_ <: T])" -> fromFuture, - "from(Future[_ <: T], Long, TimeUnit)" -> fromFuture, - "from(Future[_ <: T], Scheduler)" -> fromFuture, - "just(T)" -> "[use `items(T*)`]", - "just(T, Scheduler)" -> "[use `items(T*).subscribeOn(scheduler)`]", - "merge(Observable[_ <: T], Observable[_ <: T])" -> "merge(Observable[U])", - "merge(Observable[_ <: Observable[_ <: T]])" -> "flatten(<:<[Observable[T], Observable[Observable[U]]])", - "merge(Observable[_ <: Observable[_ <: T]], Int)" -> "flatten(Int)(<:<[Observable[T], Observable[Observable[U]]])", - "merge(Array[Observable[_ <: T]])" -> "[use `Observable.from(array).flatten`]", - "merge(Array[Observable[_ <: T]], Scheduler)" -> "[use `Observable.from(array, scheduler).flatten`]", - "merge(Iterable[_ <: Observable[_ <: T]])" -> "[use `Observable.from(iter).flatten`]", - "merge(Iterable[_ <: Observable[_ <: T]], Int)" -> "[use `Observable.from(iter).flatten(n)`]", - "merge(Iterable[_ <: Observable[_ <: T]], Int, Scheduler)" -> "[use `Observable.from(iter, scheduler).flatten(n)]", - "merge(Iterable[_ <: Observable[_ <: T]], Scheduler)" -> "[use `Observable.from(iter, scheduler).flatten`]", - "mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> "mergeDelayError(Observable[U])", - "mergeDelayError(Observable[_ <: Observable[_ <: T]])" -> "flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])", - "parallelMerge(Observable[Observable[T]], Int)" -> "parallelMerge(Int)(<:<[Observable[T], Observable[Observable[U]]])", - "parallelMerge(Observable[Observable[T]], Int, Scheduler)" -> "parallelMerge(Int, Scheduler)(<:<[Observable[T], Observable[Observable[U]]])", - "pivot(Observable[GroupedObservable[K1, GroupedObservable[K2, T]]])" -> "pivot(<:<[Observable[T], Observable[(K1, Observable[(K2, Observable[U])])]])", - "sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> "sequenceEqual(Observable[U])", - "sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> "sequenceEqualWith(Observable[U])((U, U) => Boolean)", - "range(Int, Int)" -> "[use `(start until (start + count)).toObservable` instead of `range(start, count)`]", - "range(Int, Int, Scheduler)" -> "[use `(start until (start + count)).toObservable.subscribeOn(scheduler)` instead of `range(start, count, scheduler)`]`]", - "sum(Observable[Integer])" -> "sum(Numeric[U])", - "sumDoubles(Observable[Double])" -> "sum(Numeric[U])", - "sumFloats(Observable[Float])" -> "sum(Numeric[U])", - "sumLongs(Observable[Long])" -> "sum(Numeric[U])", - "switchDo(Observable[_ <: Observable[_ <: T]])" -> deprecated, - "switchOnNext(Observable[_ <: Observable[_ <: T]])" -> "switch(<:<[Observable[T], Observable[Observable[U]]])", - "zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> "[use instance method `zip` and `map`]", - "zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]", - "zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> "[use `zip` in companion object and `map`]", - "zip(Observable[_ <: T2], Func2[_ >: T, _ >: T2, _ <: R])" -> "zipWith(Observable[U])((T, U) => R)", - "zip(Iterable[_ <: T2], Func2[_ >: T, _ >: T2, _ <: R])" -> "zipWith(Iterable[U])((T, U) => R)" - ) ++ List.iterate("T, T", 8)(s => s + ", T").map( - // all 9 overloads of startWith: - "startWith(" + _ + ")" -> "[use `Observable.items(...) ++ o`]" - ).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( - // concat 2-9 - "concat(" + _ + ")" -> "[unnecessary because we can use `++` instead or `Observable(o1, o2, ...).concat`]" - ).drop(1).toMap ++ List.iterate("T", 10)(s => s + ", T").map( - // all 10 overloads of from: - "from(" + _ + ")" -> "[use `items(T*)`]" - ).toMap ++ (3 to 9).map(i => { - // zip3-9: - val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") - val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") - ("zip(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", unnecessary) - }).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( - // merge 3-9: - "merge(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flatten` instead]" - ).drop(2).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( - // mergeDelayError 3-9: - "mergeDelayError(" + _ + ")" -> "[unnecessary because we can use `Observable(o1, o2, ...).flattenDelayError` instead]" - ).drop(2).toMap ++ (3 to 9).map(i => { - // combineLatest 3-9: - val obsArgs = (1 to i).map(j => s"Observable[_ <: T$j], ").mkString("") - val funcParams = (1 to i).map(j => s"_ >: T$j, ").mkString("") - ("combineLatest(" + obsArgs + "Func" + i + "[" + funcParams + "_ <: R])", "[If C# doesn't need it, Scala doesn't need it either ;-)]") - }).toMap ++ List.iterate("Observable[_ <: T]", 9)(s => s + ", Observable[_ <: T]").map( - // amb 2-9 - "amb(" + _ + ")" -> "[unnecessary because we can use `o1 amb o2` instead or `amb(List(o1, o2, o3, ...)`]" - ).drop(1).toMap - - def removePackage(s: String) = s.replaceAll("(\\w+\\.)+(\\w+)", "$2") - - def methodMembersToMethodStrings(members: Iterable[Symbol]): Iterable[String] = { - for (member <- members; alt <- member.asTerm.alternatives) yield { - val m = alt.asMethod - // multiple parameter lists in case of curried functions - val paramListStrs = for (paramList <- m.paramss) yield { - paramList.map( - symb => removePackage(symb.typeSignature.toString.replaceAll(",(\\S)", ", $1")) - ).mkString("(", ", ", ")") - } - val name = alt.asMethod.name.decoded - name + paramListStrs.mkString("") - } - } - - def getPublicInstanceMethods(tp: Type): Iterable[String] = { - // declarations: => only those declared in Observable - // members => also those of superclasses - methodMembersToMethodStrings(tp.declarations.filter(m => m.isMethod && m.isPublic)) - // TODO how can we filter out instance methods which were put into companion because - // of extends AnyVal in a way which does not depend on implementation-chosen name '$extension'? - .filter(! _.contains("$extension")) - // `access$000` is public. How to distinguish it from others without hard-code? - .filter(! _.contains("access$000")) - } - - // also applicable for Java types - def getPublicInstanceAndCompanionMethods(tp: Type): Iterable[String] = - getPublicInstanceMethods(tp) ++ - getPublicInstanceMethods(tp.typeSymbol.companionSymbol.typeSignature) - - def printMethodSet(title: String, tp: Type) { - println("\n" + title) - println(title.map(_ => '-') + "\n") - getPublicInstanceMethods(tp).toList.sorted.foreach(println(_)) - } - - @Ignore // because spams output - @Test def printJavaInstanceMethods(): Unit = { - printMethodSet("Instance methods of rx.Observable", - typeOf[rx.Observable[_]]) - } - - @Ignore // because spams output - @Test def printScalaInstanceMethods(): Unit = { - printMethodSet("Instance methods of rx.lang.scala.Observable", - typeOf[rx.lang.scala.Observable[_]]) - } - - @Ignore // because spams output - @Test def printJavaStaticMethods(): Unit = { - printMethodSet("Static methods of rx.Observable", - typeOf[rx.Observable[_]].typeSymbol.companionSymbol.typeSignature) - } - - @Ignore // because spams output - @Test def printScalaCompanionMethods(): Unit = { - printMethodSet("Companion methods of rx.lang.scala.Observable", - typeOf[rx.lang.scala.Observable.type]) - } - - def javaMethodSignatureToScala(s: String): String = { - s.replaceAllLiterally("Long, TimeUnit", "Duration") - .replaceAll("Action0", "() => Unit") - // nested [] can't be parsed with regex, so these will have to be added manually - .replaceAll("Action1\\[([^]]*)\\]", "$1 => Unit") - .replaceAll("Action2\\[([^]]*), ([^]]*)\\]", "($1, $2) => Unit") - .replaceAll("Func0\\[([^]]*)\\]", "() => $1") - .replaceAll("Func1\\[([^]]*), ([^]]*)\\]", "$1 => $2") - .replaceAll("Func2\\[([^]]*), ([^]]*), ([^]]*)\\]", "($1, $2) => $3") - .replaceAllLiterally("_ <: ", "") - .replaceAllLiterally("_ >: ", "") - .replaceAll("(\\w+)\\(\\)", "$1") - } - - @Ignore // because spams output - @Test def printDefaultMethodCorrespondence(): Unit = { - println("\nDefault Method Correspondence") - println( "-----------------------------\n") - val c = SortedMap(defaultMethodCorrespondence.toSeq : _*) - val len = c.keys.map(_.length).max + 2 - for ((javaM, scalaM) <- c) { - println(s""" %-${len}s -> %s,""".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) - } - } - - @Ignore // because spams output - @Test def printCorrectedMethodCorrespondence(): Unit = { - println("\nCorrected Method Correspondence") - println( "-------------------------------\n") - val c = SortedMap(correspondence.toSeq : _*) - for ((javaM, scalaM) <- c) { - println("%s -> %s,".format("\"" + javaM + "\"", "\"" + scalaM + "\"")) - } - } - - def checkMethodPresence(expectedMethods: Iterable[String], tp: Type): Unit = { - val actualMethods = getPublicInstanceAndCompanionMethods(tp).toSet - val expMethodsSorted = expectedMethods.toList.sorted - var good = 0 - var bad = 0 - for (m <- expMethodsSorted) if (actualMethods.contains(m) || m.charAt(0) == '[') { - good += 1 - } else { - bad += 1 - println(s"Warning: $m is NOT present in $tp") - } - val status = if (bad == 0) "SUCCESS" else "BAD" - println(s"$status: $bad out of ${bad+good} methods were not found in $tp") - } - - @Test def checkScalaMethodPresenceVerbose(): Unit = { - println("\nTesting that all mentioned Scala methods exist") - println( "----------------------------------------------\n") - - val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet - var good = 0 - var bad = 0 - for ((javaM, scalaM) <- SortedMap(correspondence.toSeq :_*)) { - if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') { - good += 1 - } else { - bad += 1 - println(s"Warning:") - println(s"$scalaM is NOT present in Scala Observable") - println(s"$javaM is the method in Java Observable generating this warning") - } - } - val status = if (bad == 0) "SUCCESS" else "BAD" - println(s"\n$status: $bad out of ${bad+good} methods were not found in Scala Observable") - } - - def setTodoForMissingMethods(corresp: Map[String, String]): Map[String, String] = { - val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable[_]]).toSet - for ((javaM, scalaM) <- corresp) yield - (javaM, if (actualMethods.contains(scalaM) || scalaM.charAt(0) == '[') scalaM else "[**TODO: missing**]") - } - - @Test def checkJavaMethodPresence(): Unit = { - println("\nTesting that all mentioned Java methods exist") - println( "---------------------------------------------\n") - checkMethodPresence(correspondence.keys, typeOf[rx.Observable[_]]) - } - - @Ignore // because we prefer the verbose version - @Test def checkScalaMethodPresence(): Unit = { - checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable[_]]) - } - - def scalaToJavaSignature(s: String) = - s.replaceAllLiterally("_ <:", "? extends") - .replaceAllLiterally("_ >:", "? super") - .replaceAllLiterally("[", "<") - .replaceAllLiterally("]", ">") - .replaceAllLiterally("Array<T>", "T[]") - - def escapeJava(s: String) = - s.replaceAllLiterally("<", "<") - .replaceAllLiterally(">", ">") - - @Ignore // because spams output - @Test def printMarkdownCorrespondenceTable() { - def isInteresting(p: (String, String)): Boolean = - p._1.replaceAllLiterally("()", "") != p._2 - def groupingKey(p: (String, String)): (String, String) = - (if (p._1.startsWith("average")) "average" else p._1.takeWhile(_ != '('), p._2) - def formatJavaCol(name: String, alternatives: Iterable[String]): String = { - alternatives.toList.sorted.map(scalaToJavaSignature(_)).map(s => { - if (s.length > 64) { - val toolTip = escapeJava(s) - "<span title=\"" + toolTip + "\"><code>" + name + "(...)</code></span>" - } else { - "`" + s + "`" - } - }).mkString("<br/>") - } - def formatScalaCol(s: String): String = - if (s.startsWith("[") && s.endsWith("]")) s.drop(1).dropRight(1) else "`" + s + "`" - def escape(s: String) = s.replaceAllLiterally("[", "<").replaceAllLiterally("]", ">") - - println(""" ---- -layout: comparison -title: Comparison of Scala Observable and Java Observable ---- - -Note: -* This table contains both static methods and instance methods. -* If a signature is too long, move your mouse over it to get the full signature. - - -| Java Method | Scala Method | -|-------------|--------------|""") - - val ps = setTodoForMissingMethods(correspondence) - - (for (((javaName, scalaCol), pairs) <- ps.groupBy(groupingKey(_)).toList.sortBy(_._1._1)) yield { - "| " + formatJavaCol(javaName, pairs.map(_._1)) + " | " + formatScalaCol(scalaCol) + " |" - }).foreach(println(_)) - println(s"\nThis table was generated on ${Calendar.getInstance().getTime}.") - println(s"**Do not edit**. Instead, edit `${getClass.getCanonicalName}`.") - } - -} diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ConstructorTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ConstructorTest.scala deleted file mode 100644 index 590826079f..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ConstructorTest.scala +++ /dev/null @@ -1,35 +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 scala.language.postfixOps -import org.junit.Assert._ -import org.junit.Test -import org.scalatest.junit.JUnitSuite - -class ConstructorTest extends JUnitSuite { - - @Test def toObservable() { - val xs = List(1,2,3).toObservable.toBlocking.toList - assertEquals(List(1,2,3), xs) - - val ys = Observable.from(List(1,2,3)).toBlocking.toList - assertEquals(List(1,2,3), xs) - - val zs = Observable.items(1,2,3).toBlocking.toList - assertEquals(List(1,2,3), xs) - - } -} diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/NotificationTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/NotificationTests.scala deleted file mode 100644 index 354fd71a5c..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/NotificationTests.scala +++ /dev/null @@ -1,58 +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 org.junit.{Assert, Test} -import org.junit.Assert._ -import org.scalatest.junit.JUnitSuite -import scala.concurrent.duration._ -import scala.language.postfixOps -import org.mockito.Mockito._ -import org.mockito.Matchers._ -import rx.lang.scala.Notification.{OnCompleted, OnError, OnNext} - - -class NotificationTests extends JUnitSuite { - @Test - def creation() { - - val onNext = OnNext(42) - assertEquals(42, onNext match { case OnNext(value) => value }) - - val oops = new Exception("Oops") - val onError = OnError(oops) - assertEquals(oops, onError match { case OnError(error) => error }) - - val onCompleted = OnCompleted - assertEquals((), onCompleted match { case OnCompleted => () }) - } - - @Test - def accept() { - - val onNext = OnNext(42) - assertEquals(42, onNext(x=>42, e=>4711,()=>13)) - - val oops = new Exception("Oops") - val onError = OnError(oops) - assertEquals(4711, onError(x=>42, e=>4711,()=>13)) - - val onCompleted = OnCompleted - assertEquals(13, onCompleted(x=>42, e=>4711,()=>13)) - - } -} diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala deleted file mode 100644 index 844a5e4443..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala +++ /dev/null @@ -1,349 +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 scala.collection.mutable -import scala.collection.mutable.ListBuffer -import scala.concurrent.{Future, Await} -import scala.concurrent.duration.Duration -import scala.concurrent.ExecutionContext.Implicits.global -import org.junit.Assert._ -import org.junit.{ Ignore, Test } -import org.scalatest.junit.JUnitSuite -import scala.concurrent.duration._ -import scala.language.postfixOps -import rx.lang.scala.schedulers.TestScheduler -import rx.lang.scala.subjects.BehaviorSubject -import org.mockito.Mockito._ -import org.mockito.Matchers._ - -class ObservableTests extends JUnitSuite { - - // Tests which needn't be run: - - @Ignore - def testCovariance = { - //println("hey, you shouldn't run this test") - - val o1: Observable[Nothing] = Observable.empty - val o2: Observable[Int] = o1 - val o3: Observable[App] = o1 - val o4: Observable[Any] = o2 - val o5: Observable[Any] = o3 - } - - // Tests which have to be run: - - @Test - def testDematerialize() { - val o = List(1, 2, 3).toObservable - val mat = o.materialize - val demat = mat.dematerialize - - //correctly rejected: - //val wrongDemat = Observable("hello").dematerialize - - assertEquals(demat.toBlocking.toIterable.toList, List(1, 2, 3)) -} - - @Test def TestScan() { - val xs = Observable.items(0,1,2,3) - val ys = xs.scan(0)(_+_) - assertEquals(List(0,0,1,3,6), ys.toBlocking.toList) - val zs = xs.scan((x: Int, y:Int) => x*y) - assertEquals(List(0, 0, 0, 0), zs.toBlocking.toList) - } - - // Test that Java's firstOrDefault propagates errors. - // If this changes (i.e. it suppresses errors and returns default) then Scala's firstOrElse - // should be changed accordingly. - @Test def testJavaFirstOrDefault() { - assertEquals(1, rx.Observable.from(1, 2).firstOrDefault(10).toBlocking().single) - assertEquals(10, rx.Observable.empty().firstOrDefault(10).toBlocking().single) - val msg = "msg6251" - var receivedMsg = "none" - try { - rx.Observable.error(new Exception(msg)).firstOrDefault(10).toBlocking().single - } catch { - case e: Exception => receivedMsg = e.getCause().getMessage() - } - assertEquals(receivedMsg, msg) - } - - @Test def testFirstOrElse() { - def mustNotBeCalled: String = sys.error("this method should not be called") - def mustBeCalled: String = "this is the default value" - assertEquals("hello", Observable.items("hello").firstOrElse(mustNotBeCalled).toBlocking.single) - assertEquals("this is the default value", Observable.empty.firstOrElse(mustBeCalled).toBlocking.single) - } - - @Test def testTestWithError() { - val msg = "msg6251" - var receivedMsg = "none" - try { - Observable.error[Int](new Exception(msg)).firstOrElse(10).toBlocking.single - } catch { - case e: Exception => receivedMsg = e.getCause().getMessage() - } - assertEquals(receivedMsg, msg) - } - - @Test def testFromFuture() { - val o = Observable from Future { 5 } - assertEquals(5, o.toBlocking.single) - } - - @Test def testFromFutureWithDelay() { - val o = Observable from Future { Thread.sleep(200); 42 } - assertEquals(42, o.toBlocking.single) - } - - @Test def testFromFutureWithError() { - val err = new Exception("ooops42") - val o: Observable[Int] = Observable from Future { Thread.sleep(200); throw err } - assertEquals(List(Notification.OnError(err)), o.materialize.toBlocking.toList) - } - - @Test def testFromFutureWithSubscribeOnlyAfterCompletion() { - val f = Future { Thread.sleep(200); 6 } - val o = Observable from f - val res = Await.result(f, Duration.Inf) - assertEquals(6, res) - assertEquals(6, o.toBlocking.single) - } - - @Test def testJoin() { - val xs = Observable.items(1,2,3) - val ys = Observable.items("a") - val zs = xs.join(ys)(_ => Observable.never, _ => Observable.never, (x, y) => y + x) - assertEquals(List("a1", "a2", "a3"),zs.toBlocking.toList) - } - - @Test def testTimestampWithScheduler() { - val c = 10 - val s = TestScheduler() - val o1 = Observable interval (1.milliseconds, s) map (_ + 1) - val o2 = o1 timestamp s - val l = ListBuffer[(Long, Long)]() - o2.subscribe ( - onNext = (l += _) - ) - s advanceTimeTo c.milliseconds - val (l1, l2) = l.toList.unzip - assertTrue(l1.size == c) - assertEquals(l2, l1) - } - - @Test def testHead() { - val o: Observable[String] = List("alice", "bob", "carol").toObservable.head - assertEquals(List("alice"), o.toBlocking.toList) - } - - @Test(expected = classOf[NoSuchElementException]) - def testHeadWithEmptyObservable() { - val o: Observable[String] = List[String]().toObservable.head - o.toBlocking.toList - } - - @Test def testTail() { - val o: Observable[String] = List("alice", "bob", "carol").toObservable.tail - assertEquals(List("bob", "carol"), o.toBlocking.toList) - assertEquals(List("bob", "carol"), o.toBlocking.toList) - } - - @Test(expected = classOf[UnsupportedOperationException]) - def testTailWithEmptyObservable() { - val o: Observable[String] = List[String]().toObservable.tail - o.toBlocking.toList - } - - @Test - def testZipWithIndex() { - val o = List("alice", "bob", "carol").toObservable.zipWithIndex.map(_._2) - assertEquals(List(0, 1, 2), o.toBlocking.toList) - assertEquals(List(0, 1, 2), o.toBlocking.toList) - } - - @Test - def testSingleOrElse() { - val o = Observable.items(1).singleOrElse(2) - assertEquals(1, o.toBlocking.single) - } - - @Test - def testSingleOrElseWithEmptyObservable() { - val o: Observable[Int] = Observable.empty.singleOrElse(1) - assertEquals(1, o.toBlocking.single) - } - - @Test(expected = classOf[IllegalArgumentException]) - def testSingleOrElseWithTooManyItems() { - Observable.items(1, 2).singleOrElse(1).toBlocking.single - } - - @Test - def testSingleOrElseWithCallByName() { - var called = false - val o: Observable[Int] = Observable.empty.singleOrElse { - called = true - 1 - } - assertFalse(called) - o.subscribe() - assertTrue(called) - } - - @Test - def testSingleOrElseWithCallByName2() { - var called = false - val o = Observable.items(1).singleOrElse { - called = true - 2 - } - assertFalse(called) - o.subscribe() - assertFalse(called) - } - - @Test - def testOrElse() { - val o = Observable.items(1, 2, 3).orElse(4) - assertEquals(List(1, 2, 3), o.toBlocking.toList) - } - - @Test - def testOrElseWithEmpty() { - val o = Observable.empty.orElse(-1) - assertEquals(List(-1), o.toBlocking.toList) - } - - @Test - def testToMultimap() { - val o = Observable.items("a", "b", "cc", "dd").toMultimap(_.length) - val expected = Map(1 -> List("a", "b"), 2 -> List("cc", "dd")) - assertEquals(expected, o.toBlocking.single) - } - - @Test - def testToMultimapWithValueSelector() { - val o = Observable.items("a", "b", "cc", "dd").toMultimap(_.length, s => s + s) - val expected = Map(1 -> List("aa", "bb"), 2 -> List("cccc", "dddd")) - assertEquals(expected, o.toBlocking.single) - } - - @Test - def testToMultimapWithMapFactory() { - val m = mutable.Map[Int, mutable.Buffer[String]]() - val o = Observable.items("a", "b", "cc", "dd").toMultimap(_.length, s => s, () => m) - val expected = Map(1 -> List("a", "b"), 2 -> List("cc", "dd")) - val r = o.toBlocking.single - // r should be the same instance created by the `mapFactory` - assertTrue(m eq r) - assertEquals(expected, r) - } - - @Test - def testToMultimapWithBufferFactory() { - val m = mutable.Map[Int, mutable.Buffer[String]]() - val ls = List(mutable.Buffer[String](), mutable.Buffer[String]()) - val o = Observable.items("a", "b", "cc", "dd").toMultimap(_.length, s => s, () => m, (i: Int) => ls(i - 1)) - val expected = Map(1 -> List("a", "b"), 2 -> List("cc", "dd")) - val r = o.toBlocking.single - // r should be the same instance created by the `mapFactory` - assertTrue(m eq r) - // r(1) should be the same instance created by the first calling `bufferFactory` - assertTrue(ls(0) eq r(1)) - // r(2) should be the same instance created by the second calling `bufferFactory` - assertTrue(ls(1) eq r(2)) - assertEquals(expected, r) - } - - @Test - def testCreate() { - var called = false - val o = Observable.create[String](observer => { - observer.onNext("a") - observer.onNext("b") - observer.onNext("c") - observer.onCompleted() - Subscription { - called = true - } - }) - assertEquals(List("a", "b", "c"), o.toBlocking.toList) - assertTrue(called) - } - - @Test - def testToTraversable() { - val o = Observable.items(1, 2, 3).toTraversable - assertEquals(Seq(1, 2, 3), o.toBlocking.single) - } - - @Test - def testToList() { - val o = Observable.items(1, 2, 3).toList - assertEquals(Seq(1, 2, 3), o.toBlocking.single) - } - - @Test - def testToIterable() { - val o = Observable.items(1, 2, 3).toIterable - assertEquals(Seq(1, 2, 3), o.toBlocking.single) - } - - @Test - def testToIterator() { - val o = Observable.items(1, 2, 3).toIterator - assertEquals(Seq(1, 2, 3), o.toBlocking.single.toSeq) - } - - @Test - def testToStream() { - val o = Observable.items(1, 2, 3).toStream - assertEquals(Seq(1, 2, 3), o.toBlocking.single) - } - - @Test - def testToIndexedSeq() { - val o = Observable.items(1, 2, 3).toIndexedSeq - assertEquals(Seq(1, 2, 3), o.toBlocking.single) - } - - @Test - def testToBuffer() { - val o = Observable.items(1, 2, 3).toBuffer - assertEquals(Seq(1, 2, 3), o.toBlocking.single) - } - - @Test - def testToSet() { - val o = Observable.items(1, 2, 2).toSet - assertEquals(Set(1, 2), o.toBlocking.single) - } - - @Test - def testToVector() { - val o = Observable.items(1, 2, 3).toVector - assertEquals(Seq(1, 2, 3), o.toBlocking.single) - } - - @Test - def testToArray() { - val o = Observable.items(1, 2, 3).toArray - assertArrayEquals(Array(1, 2, 3), o.toBlocking.single) - } -} diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/SubjectTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/SubjectTests.scala deleted file mode 100644 index f8d72e3520..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/SubjectTests.scala +++ /dev/null @@ -1,323 +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 org.junit.{Assert, Test} -import org.scalatest.junit.JUnitSuite -import scala.concurrent.duration._ -import scala.language.postfixOps -import rx.lang.scala.schedulers.TestScheduler -import rx.lang.scala.subjects.{AsyncSubject, ReplaySubject, BehaviorSubject} -import org.mockito.Mockito._ -import org.mockito.Matchers._ -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Assert.assertFalse -import org.junit.Ignore -import org.junit.Test -import org.scalatest.junit.JUnitSuite - -class SubjectTest extends JUnitSuite { - - @Test def SubjectIsAChannel() { - - var lastA: Integer = null - var errorA: Throwable = null - var completedA: Boolean = false - val observerA = Observer[Integer]( - (next: Integer) => { lastA = next }, - (error: Throwable) => { errorA = error }, - () => { completedA = true } - ) - - var lastB: Integer = null - var errorB: Throwable = null - var completedB: Boolean = false - val observerB = Observer[Integer]( - (next: Integer) => { lastB = next }, - (error: Throwable) => { errorB = error }, - () => { completedB = true } - ) - - var lastC: Integer = null - var errorC: Throwable = null - var completedC: Boolean = false - val observerC = Observer[Integer]( - (next: Integer) => { lastC = next }, - (error: Throwable) => { errorC = error }, - () => { completedC = true } - ) - - val channel: Subject[Integer] = Subject[Integer]() - - val a = channel(observerA) - val b = channel(observerB) - - assertEquals(null, lastA) - assertEquals(null, lastB) - - channel.onNext(42) - - assertEquals(42, lastA) - assertEquals(42, lastB) - - a.unsubscribe() - channel.onNext(4711) - - assertEquals(42, lastA) - assertEquals(4711, lastB) - - channel.onCompleted() - - assertFalse(completedA) - assertTrue(completedB) - assertEquals(42, lastA) - assertEquals(4711, lastB) - - val c = channel.subscribe(observerC) - channel.onNext(13) - - assertEquals(null, lastC) - assertTrue(completedC) - - assertFalse(completedA) - assertTrue(completedB) - assertEquals(42, lastA) - assertEquals(4711, lastB) - - channel.onError(new Exception("!")) - - assertEquals(null, lastC) - assertTrue(completedC) - - assertFalse(completedA) - assertTrue(completedB) - assertEquals(42, lastA) - assertEquals(4711, lastB) - } - - @Test def ReplaySubjectIsAChannel() { - - val channel = ReplaySubject[Integer] - - var lastA: Integer = null - var errorA, completedA: Boolean = false - val a = channel.subscribe(x => { lastA = x}, e => { errorA = true} , () => { completedA = true }) - - var lastB: Integer = null - var errorB, completedB: Boolean = false - - val b = channel(new Observer[Integer] { - override def onNext(value: Integer): Unit = { lastB = value } - override def onError(error: Throwable): Unit = { errorB = true } - override def onCompleted(): Unit = { completedB = true } - }) - - channel.onNext(42) - - assertEquals(42, lastA) - assertEquals(42, lastB) - - a.unsubscribe() - - channel.onNext(4711) - - assertEquals(42, lastA) - assertEquals(4711, lastB) - - channel.onCompleted() - - assertEquals(42, lastA) - assertFalse(completedA) - assertFalse(errorA) - - assertEquals(4711, lastB) - assertTrue(completedB) - assertFalse(errorB) - - var lastC: Integer = null - var errorC, completedC: Boolean = false - val c = channel.subscribe(x => { lastC = x}, e => { errorC = true} , () => { completedC = true }) - - assertEquals(4711, lastC) - assertTrue(completedC) - assertFalse(errorC) - - channel.onNext(13) - - assertEquals(42, lastA) - assertFalse(completedA) - assertFalse(errorA) - - assertEquals(4711, lastB) - assertTrue(completedB) - assertFalse(errorB) - - assertEquals(4711, lastC) - assertTrue(completedC) - assertFalse(errorC) - - channel.onError(new Exception("Boom")) - - assertEquals(42, lastA) - assertFalse(completedA) - assertFalse(errorA) - - assertEquals(4711, lastB) - assertTrue(completedB) - assertFalse(errorB) - - assertEquals(4711, lastC) - assertTrue(completedC) - assertFalse(errorC) - } - - @Test def BehaviorSubjectIsACache() { - - val channel = BehaviorSubject(2013) - - var lastA: Integer = null - var errorA, completedA: Boolean = false - val a = channel.subscribe(x => { lastA = x}, e => { errorA = true} , () => { completedA = true }) - - var lastB: Integer = null - var errorB, completedB: Boolean = false - val b = channel.subscribe(x => { lastB = x}, e => { errorB = true} , () => { completedB = true }) - - assertEquals(2013, lastA) - assertEquals(2013, lastB) - - channel.onNext(42) - - assertEquals(42, lastA) - assertEquals(42, lastB) - - a.unsubscribe() - - channel.onNext(4711) - - assertEquals(42, lastA) - assertEquals(4711, lastB) - - channel.onCompleted() - - var lastC: Integer = null - var errorC, completedC: Boolean = false - val c = channel.subscribe(x => { lastC = x}, e => { errorC = true} , () => { completedC = true }) - - assertEquals(null, lastC) - assertTrue(completedC) - assertFalse(errorC) - - channel.onNext(13) - - assertEquals(42, lastA) - assertFalse(completedA) - assertFalse(errorA) - - assertEquals(4711, lastB) - assertTrue(completedB) - assertFalse(errorB) - - assertEquals(null, lastC) - assertTrue(completedC) - assertFalse(errorC) - - channel.onError(new Exception("Boom")) - - assertEquals(42, lastA) - assertFalse(completedA) - assertFalse(errorA) - - assertEquals(4711, lastB) - assertTrue(completedB) - assertFalse(errorB) - - assertEquals(null, lastC) - assertTrue(completedC) - assertFalse(errorC) - - } - - @Test def AsyncSubjectIsAFuture() { - - val channel = AsyncSubject[Int]() - - var lastA: Integer = null - var errorA, completedA: Boolean = false - val a = channel.subscribe(x => { lastA = x}, e => { errorA = true} , () => { completedA = true }) - - var lastB: Integer = null - var errorB, completedB: Boolean = false - val b = channel.subscribe(x => { lastB = x}, e => { errorB = true} , () => { completedB = true }) - - channel.onNext(42) - - Assert.assertEquals(null, lastA) - Assert.assertEquals(null, lastB) - - a.unsubscribe() - channel.onNext(4711) - channel.onCompleted() - - Assert.assertEquals(null, lastA) - Assert.assertFalse(completedA) - Assert.assertFalse(errorA) - - Assert.assertEquals(4711, lastB) - Assert.assertTrue(completedB) - Assert.assertFalse(errorB) - - - var lastC: Integer = null - var errorC, completedC: Boolean = false - val c = channel.subscribe(x => { lastC = x}, e => { errorC = true} , () => { completedC = true }) - - Assert.assertEquals(4711, lastC) - Assert.assertTrue(completedC) - Assert.assertFalse(errorC) - - channel.onNext(13) - - Assert.assertEquals(null, lastA) - Assert.assertFalse(completedA) - Assert.assertFalse(errorA) - - Assert.assertEquals(4711, lastB) - Assert.assertTrue(completedB) - Assert.assertFalse(errorB) - - Assert.assertEquals(4711, lastC) - Assert.assertTrue(completedC) - Assert.assertFalse(errorC) - - channel.onError(new Exception("Boom")) - - Assert.assertEquals(null, lastA) - Assert.assertFalse(completedA) - Assert.assertFalse(errorA) - - Assert.assertEquals(4711, lastB) - Assert.assertTrue(completedB) - Assert.assertFalse(errorB) - - Assert.assertEquals(4711, lastC) - Assert.assertTrue(completedC) - Assert.assertFalse(errorC) - - } - -} \ No newline at end of file diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/SubscriberTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/SubscriberTests.scala deleted file mode 100644 index 60eb9fecd7..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/SubscriberTests.scala +++ /dev/null @@ -1,60 +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 org.junit.Test -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertTrue -import org.scalatest.junit.JUnitSuite - -class SubscriberTests extends JUnitSuite { - - @Test def testIssue1173() { - // https://github.com/Netflix/RxJava/issues/1173 - val subscriber = Subscriber((n: Int) => println(n)) - assertNotNull(subscriber.asJavaObserver) - assertNotNull(subscriber.asJavaSubscription) - assertNotNull(subscriber.asJavaSubscriber) - } - - @Test def testUnsubscribeForSubscriber() { - var innerSubscriber: Subscriber[Int] = null - val o = Observable[Int](subscriber => { - Observable[Int](subscriber => { - innerSubscriber = subscriber - }).subscribe(subscriber) - }) - o.subscribe().unsubscribe() - // If we unsubscribe outside, the inner Subscriber should also be unsubscribed - assertTrue(innerSubscriber.isUnsubscribed) - } - - @Test def testBlockCallbackOnlyOnce() { - var called = false - val o = Observable[Int](subscriber => { - subscriber.add({ called = !called }) - }) - - val subscription = o.subscribe() - subscription.unsubscribe() - subscription.unsubscribe() - - // Even if called multiple times, callback is only called once - assertTrue(called) - assertTrue(subscription.isUnsubscribed) - } - -} diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/SubscriptionTests.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/SubscriptionTests.scala deleted file mode 100644 index b53d4fe265..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/SubscriptionTests.scala +++ /dev/null @@ -1,186 +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 org.junit.{Assert, Test} -import org.junit.Assert -import org.scalatest.junit.JUnitSuite -import scala.concurrent.duration._ -import scala.language.postfixOps -import org.mockito.Mockito._ -import org.mockito.Matchers._ -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Assert.assertFalse -import rx.lang.scala.subscriptions.{SerialSubscription, MultipleAssignmentSubscription, CompositeSubscription} - - - -class SubscriptionTests extends JUnitSuite { - @Test - def subscriptionCreate() { - - val subscription = Subscription() - - assertFalse(subscription.isUnsubscribed) - - subscription.unsubscribe() - - assertTrue(subscription.isUnsubscribed) - } - - @Test - def subscriptionUnsubscribeIdempotent() { - - var called = false - - val subscription = Subscription{ called = !called } - - assertFalse(called) - assertFalse(subscription.isUnsubscribed) - - subscription.unsubscribe() - - assertTrue(called) - assertTrue(subscription.isUnsubscribed) - - subscription.unsubscribe() - - assertTrue(called) - assertTrue(subscription.isUnsubscribed) - } - - @Test - def compositeSubscriptionAdd() { - - val s0 = Subscription() - val s1 = Subscription() - - val composite = CompositeSubscription() - - assertFalse(composite.isUnsubscribed) - - composite += s0 - composite += s1 - - composite.unsubscribe() - - assertTrue(composite.isUnsubscribed) - assertTrue(s0.isUnsubscribed) - assertTrue(s1.isUnsubscribed) - - val s2 = Subscription{} - - assertFalse(s2.isUnsubscribed) - - composite += s2 - - assertTrue(s2.isUnsubscribed) - - } - - @Test - def compositeSubscriptionRemove() { - - val s0 = Subscription() - val composite = CompositeSubscription() - - composite += s0 - assertFalse(s0.isUnsubscribed) - - composite -= s0 - assertTrue(s0.isUnsubscribed) - - composite.unsubscribe() - - assertTrue(composite.isUnsubscribed) - assertTrue(s0.isUnsubscribed) - } - - @Test - def multiAssignmentSubscriptionAdd() { - - val s0 = Subscription() - val s1 = Subscription() - val multiple = MultipleAssignmentSubscription() - - assertFalse(multiple.isUnsubscribed) - assertFalse(s0.isUnsubscribed) - assertFalse(s1.isUnsubscribed) - - multiple.subscription = s0 - - assertFalse(s0.isUnsubscribed) - assertFalse(s1.isUnsubscribed) - - multiple.subscription = s1 - - assertFalse(s0.isUnsubscribed) // difference with SerialSubscription - assertFalse(s1.isUnsubscribed) - - multiple.unsubscribe() - - assertTrue(multiple.isUnsubscribed) - assertFalse(s0.isUnsubscribed) - assertTrue(s1.isUnsubscribed) - - val s2 = Subscription() - - assertFalse(s2.isUnsubscribed) - - multiple.subscription = s2 - - assertTrue(s2.isUnsubscribed) - assertFalse(s0.isUnsubscribed) - } - - @Test - def serialSubscriptionAdd() { - - val s0 = Subscription() - val s1 = Subscription() - val serial = SerialSubscription() - - assertFalse(serial.isUnsubscribed) - assertFalse(s0.isUnsubscribed) - assertFalse(s1.isUnsubscribed) - - serial.subscription = s0 - - assertFalse(s0.isUnsubscribed) - assertFalse(s1.isUnsubscribed) - - serial.subscription = s1 - - assertTrue(s0.isUnsubscribed) // difference with MultipleAssignmentSubscription - assertFalse(s1.isUnsubscribed) - - serial.unsubscribe() - - assertTrue(serial.isUnsubscribed) - assertTrue(s1.isUnsubscribed) - - val s2 = Subscription() - - assertFalse(s2.isUnsubscribed) - - serial.subscription = s2 - - assertTrue(s2.isUnsubscribed) - } - -} diff --git a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/observables/BlockingObservableTest.scala b/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/observables/BlockingObservableTest.scala deleted file mode 100644 index d488150f46..0000000000 --- a/language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/observables/BlockingObservableTest.scala +++ /dev/null @@ -1,152 +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.observables - -import scala.concurrent.Await -import scala.concurrent.duration._ -import org.junit.Assert._ -import org.junit.Test -import org.scalatest.junit.JUnitSuite -import scala.language.postfixOps -import rx.lang.scala.Observable - -class BlockingObservableTest extends JUnitSuite { - - @Test - def testSingleOption() { - val o = Observable.items(1) - assertEquals(Some(1), o.toBlocking.singleOption) - } - - @Test - def testSingleOptionWithEmpty() { - val o = Observable.empty - assertEquals(None, o.toBlocking.singleOption) - } - - @Test(expected = classOf[IllegalArgumentException]) - def testSingleOptionWithMultipleItems() { - Observable.items(1, 2).toBlocking.singleOption - } - - @Test - def testSingleOrElse() { - val o = Observable.items(1) - assertEquals(1, o.toBlocking.singleOrElse(2)) - } - - @Test - def testSingleOrElseWithEmpty() { - val o = Observable.empty - assertEquals(2, o.toBlocking.singleOrElse(2)) - } - - @Test(expected = classOf[IllegalArgumentException]) - def testSingleOrElseWithMultipleItems() { - Observable.items(1, 2).toBlocking.singleOrElse(2) - } - - @Test - def testHeadOption() { - val o = Observable.items(1) - assertEquals(Some(1), o.toBlocking.headOption) - } - - @Test - def testHeadOptionWithEmpty() { - val o = Observable.empty - assertEquals(None, o.toBlocking.headOption) - } - - @Test - def testHeadOptionWithMultipleItems() { - val o = Observable.items(1, 2) - assertEquals(Some(1), o.toBlocking.headOption) - } - - @Test - def testHeadOrElse() { - val o = Observable.items(1) - assertEquals(1, o.toBlocking.headOrElse(2)) - } - - @Test - def testHeadOrElseWithEmpty() { - val o = Observable.empty - assertEquals(2, o.toBlocking.headOrElse(2)) - } - - @Test - def testHeadOrElseWithMultipleItems() { - val o = Observable.items(1, 2) - assertEquals(1, o.toBlocking.headOrElse(2)) - } - - @Test - def testLastOption() { - val o = Observable.items(1) - assertEquals(Some(1), o.toBlocking.lastOption) - } - - @Test - def testLastOptionWithEmpty() { - val o = Observable.empty - assertEquals(None, o.toBlocking.lastOption) - } - - @Test - def testLastOptionWithMultipleItems() { - val o = Observable.items(1, 2) - assertEquals(Some(2), o.toBlocking.lastOption) - } - - @Test - def testLastOrElse() { - val o = Observable.items(1) - assertEquals(1, o.toBlocking.lastOrElse(2)) - } - - @Test - def testLastOrElseWithEmpty() { - val o = Observable.empty - assertEquals(2, o.toBlocking.lastOrElse(2)) - } - - @Test - def testLastOrElseWithMultipleItems() { - val o = Observable.items(1, 2) - assertEquals(2, o.toBlocking.lastOrElse(3)) - } - - @Test - def testToFuture() { - val o = Observable.items(1) - val r = Await.result(o.toBlocking.toFuture, 10 seconds) - assertEquals(1, r) - } - - @Test(expected = classOf[NoSuchElementException]) - def testToFutureWithEmpty() { - val o = Observable.empty - Await.result(o.toBlocking.toFuture, 10 seconds) - } - - @Test(expected = classOf[IllegalArgumentException]) - def testToFutureWithMultipleItems() { - val o = Observable.items(1, 2) - Await.result(o.toBlocking.toFuture, 10 seconds) - } -} diff --git a/operators.html b/operators.html deleted file mode 100644 index ceb67abec5..0000000000 --- a/operators.html +++ /dev/null @@ -1,656 +0,0 @@ -<!DOCTYPE html> -<html lang="en-US"> - <head> - <meta http-equiv="Content-type" content="text/html;charset=utf-8" /> - <meta name="author" content="PLP Consulting" /> - <title>A tree of RxJava Observable operators - - - -

- This tree can help you find the Observable operator you’re looking for. -

-
-
-
I want to create a new Observable
-
-
that emits a particular item
-
just( )
-
-
that was returned from a function called at subscribe-time
-
start( )
-
-
anew for each subscriber
-
toAsync( )
-
-
that was returned from an Action called at subscribe-time
-
fromAction( )
-
that was returned from a Callable called at subscribe-time
-
fromCallable( )
-
that was returned from a Runnable called at subscribe-time
-
fromRunnable( )
-
after a specified delay
-
timer( )
-
-
that emits a particular set of 1–10 items
-
from( )
-
that obtains its sequence from an Array or Iterable
-
from( )
-
by retrieving it from a Future
-
deferFuture( )
-
that obtains its sequence from a Future
-
from( )
-
-
with a timeout
-
from( )
-
-
that obtains its sequence from an Action called periodically
-
runAsync( )
-
that emits a sequence of items repeatedly
-
repeat( )
-
-
as long as a predicate remains true
-
whileDo( )
-
-
but at least once, no matter what
-
doWhile( )
-
-
-
from scratch, with custom logic
-
create( )
-
for each observer that subscribes
-
defer( )
-
that emits a sequence of integers
-
range( )
-
-
at particular intervals of time
-
interval( )
-
-
after a specified delay
-
timer( )
-
-
-
that completes without emitting items
-
empty( )
-
that does nothing at all
-
never( )
-
- -
I want to create an Observable by combining other Observables
-
-
and emitting all of the items from all of the Observables in whatever order they are received
-
-
where the source Observables are passed to the operator as parameters
-
merge(…)
-
where the source Observables are found in an Array
-
merge(sequences)
-
where the source Observables are found in an Iterable or Observable
-
merge(sequences)
-
-
but I only want to process a certain number of them at once
-
merge(sequences,maxConcurrent)
-
-
but not forwarding any error notifications until all source Observables have terminated
-
mergeDelayError( )
-
-
and emitting all of the items from all of the Observables, one Observable at a time
-
concat( )
-
by combining the items from two or more Observables sequentially to come up with new items to emit
-
-
whenever each of the Observables has emitted a new item
-
zip( )
-
whenever any of the Observables has emitted a new item
-
combineLatest( )
-
whenever an item is emitted by one Observable in a window defined by an item emitted by another
-
join( )
-
-
based on an Observable that emits all items that have fallen in such a window
-
groupJoin( )
-
-
by means of Pattern and Plan intermediaries
-
and/then/when
-
-
and emitting the items from only the most-recently emitted of those Observables
-
switchOnNext( )
-
and mirroring only one of those Observables (which one depends on a parameter I am passed)
-
switchCase( )
-
reducing an Observable that emits many Observables to one that emits as many Observables as I have processes to process them on
-
parallelMerge(…)
-
- -
I want emit the items from an Observable after transforming them
-
-
one at a time with a function
-
map( )
-
by casting them to a particular type
-
cast( )
-
by emitting all of the items emitted by corresponding Observables
-
flatMap( )
-
-
combined with the original items by means of a function
-
mergeMap(collectionSelector,resultSelector)
-
-
by emitting all of the items in corresponding Iterables
-
mergeMapIterable(collectionSelector)
-
-
combined with the original items by means of a function
-
mergeMapIterable(collectionSelector,resultSelector)
-
-
based on all of the items that preceded them
-
scan( )
-
by combining them sequentially with the items in an Iterable by means of a function
-
zip(iterable,zipFunction)
-
by attaching a timestamp to them
-
timestamp( )
-
into an indicator of the amount of time that lapsed before the emission of the item
-
timeInterval( )
-
- -
I want to shift the items emitted by an Observable forward in time before reemitting them
-
delay(delay,unit)
-
-
with the amount of the shift calculated on a per-item basis
-
delay(itemDelay)
-
-
and the initial subscription to the Observable shifted as well
-
delay(subscriptionDelay,itemDelay)
-
-
- -
I want to transform items and notifications from an Observable into items and reemit them
-
-
by emitting all of the items emitted by corresponding Observables
-
mergeMap( )
-
by wrapping them in Notification objects
-
materialize( )
-
-
which I can then unwrap again with
-
dematerialize( )
-
-
- -
I want to ignore all items emitted by an Observable and only pass along its completed/error notification
-
ignoreElements( )
- -
I want to mirror an Observable but prefix items to its sequence
-
-
obtained from an Array or Iterable
-
startWith(values)
-
obtained from an Observable
-
startWith(values)
-
passed as parameters to the operator
-
startWith(…)
-
only if its sequence is empty
-
defaultIfEmpty(…)
-
- -
I want to collect items from an Observable and reemit them as buffers of items
-
-
with a maximum number of items per buffer
-
buffer(count)
-
-
and starting every n items
-
buffer(count,skip)
-
-
each time a second Observable emits an item
-
buffer(boundary)
-
-
with buffers given an initial capacity for efficiency reasons
-
buffer(boundary,initialCapacity)
-
where that second Observable is returned from a function I supply
-
buffer(bufferClosingSelector)
-
-
and operates on the emission of a third Observable that opens the buffer
-
buffer(bufferOpenings,bufferClosingSelector)
-
-
-
at periodic intervals
-
buffer(timespan,unit)
-
-
or when a certain maximum number of items fill the buffer
-
buffer(timespan,unit,count)
-
for a certain period of time after the interval begins
-
buffer(timespan,timeshift,unit)
-
-
containing only the last items emitted
-
-
that is, the last n items
-
takeLastBuffer(count)
-
-
emitted during a window of time before the Observable completed
-
takeLastBuffer(count,time,unit)
-
-
during a window of time before the Observable completed
-
takeLastBuffer(time,unit)
-
-
- -
I want to split one Observable into multiple Observables
-
-
with a maximum number of items per sub-Observable
-
window(count)
-
-
and starting every n items
-
window(count,skip)
-
-
each time a second Observable emits an item
-
window(boundary)
-
-
where that second Observable is returned from a function I supply
-
window(closingSelector)
-
-
and operates on the emission of a third Observable that starts the sub-Observable
-
window(windowOpenings,closingSelector)
-
-
-
at periodic intervals
-
window(timespan,unit)
-
-
or when a certain maximum number of items have been emitted on the sub-Observable
-
window(timespan,unit,count)
-
for a certain period of time after the interval begins
-
window(timespan,timeshift,unit)
-
-
so that similar items end up on the same Observable
-
groupBy( )
-
-
but periodically completing some of those Observables even if the source is not complete
-
groupByUntil(keySelector,durationSelector)
-
-
and transforming the items before emitting them on those Observables
-
groupByUntil(keySelector,valueSelector,durationSelector)
-
and then collecting similarly grouped Observables back together again
-
pivot( )
-
-
-
- -
I want to retrieve from an Observable
-
-
the last item emitted before it completed
-
last( )
-
-
or a default item if none were emitted
-
lastOrDefault( )
-
that matches a predicate
-
last(predicate)
-
-
or a default item if none did
-
lastOrDefault(predicate)
-
-
-
the sole item it emitted
-
-
or an exception if it did not emit exactly one
-
single( )
-
-
or rather a default item if it did not emit any
-
singleOrDefault( )
-
-
that matches a predicate, or an exception if exactly one did not
-
single(predicate)
-
-
or rather a default item if none did
-
singleOrDefault(predicate)
-
-
-
the first item it emitted
-
first( )
-
-
or a default item if none were emitted
-
firstOrDefault( )
-
that matches a predicate
-
first(predicate)
-
-
or a default item if none did
-
firstOrDefault(predicate)
-
-
-
- -
I want to reemit only certain items from an Observable
-
-
by filtering out those that do not match some predicate
-
filter( )
-
by filtering out those that are not of a particular type
-
ofType( )
-
that is, only the first item
-
-
or notify of an error if the source is empty
-
first( )
-
or a default value if the source is empty
-
firstOrDefault(defaultValue)
-
that matches a predicate
-
takeFirst(predicate)
-
-
or notify of an error if none do
-
first(predicate)
-
or a default value if none do
-
firstOrDefault(defaultValue,predicate)
-
-
-
that is, only the first items
-
-
that is, the first n items
-
take(num)
-
that is, items emitted by the source during an initial period of time
-
take(time,unit)
-
-
that is, only the last item
-
last( )
-
-
that meets some predicate
-
last(predicate)
-
-
or a default item if none do
-
lastOrDefault(predicate)
-
-
or a default item the source emits nothing
-
lastOrDefault( )
-
-
that is, only item n
-
elementAt( )
-
-
or a default value if there is no item n
-
elementAtOrDefault( )
-
-
that is, only those items after the first items
-
-
that is, after the first n items
-
skip(num)
-
-
or until one of those items matches a predicate
-
skipWhileWithIndex( )
-
-
that is, until one of those items matches a predicate
-
skipWhile( )
-
that is, after an initial period of time
-
skip(time,unit)
-
that is, after a second Observable emits an item
-
skipUntil( )
-
-
that is, those items except the last items
-
-
that is, except the last n items
-
skipLast( )
-
-
or until one of those items matches a predicate
-
takeWhileWithIndex( )
-
-
that is, until one of those items matches a predicate
-
takeWhile( )
-
that is, except items emitted during a period of time before the source completes
-
skipLast(time,unit)
-
that is, except items emitted after a second Observable emits an item
-
takeUntil( )
-
-
by sampling the Observable periodically
-
-
based on a timer
-
-
and emitting the most-recently emitted item in the period
-
sample(time,unit)
-
and emitting the first-emitted item in the period
-
throttleFirst(time,unit)
-
-
based on emissions from another Observable
-
-
and emitting the most-recently emitted item in the period
-
sample(sampler)
-
and emitting the first-emitted item in the period
-
throttleFirst(sampler)
-
-
-
by only emitting items that are not followed by other items within some duration
-
-
based on a timer
-
throttleWithTimeout(time,unit)
-
based on emissions from another Observable
-
debounce(debounceSelector)
-
-
by suppressing items that are duplicates of already-emitted items
-
distinct( )
-
-
according to a particular function
-
distinct(keySelector)
-
if they immediately follow the item they are duplicates of
-
distinctUntilChanged( )
-
-
according to a particular function
-
distinctUntilChanged(keySelector)
-
-
-
by delaying my subscription to it for some time after it begins emitting items
-
delaySubscription(delay,unit)
-
- -
I want to reemit items from an Observable only on condition
-
-
that it was the first of a collection of Observables to emit an item
-
amb( )
-
that some predicate is true
-
ifThen( )
-
- -
I want to evaluate the entire sequence of items emitted by an Observable
-
-
and emit a single boolean indicating if all of the items pass some test
-
all( )
-
and emit a single boolean indicating if any of the items pass some test
-
contains( )
-
and emit a single boolean indicating if the Observable emitted any items
-
exists( )
-
and emit a single boolean indicating if the Observable emitted no items
-
isEmpty( )
-
and emit a single boolean indicating if the sequence is identical to one emitted by a second Observable
-
sequenceEqual( )
-
and emit the average of all of their values
-
averageType( )
-
and emit the sum of all of their values
-
sumType( )
-
and emit a number indicating how many items were in the sequence
-
[long]count( )
-
and emit the item with the maximum value
-
max( )
-
-
according to some value-calculating function
-
maxBy( )
-
-
and emit the item with the minimum value
-
min( )
-
-
according to some value-calculating function
-
minBy( )
-
-
by applying an aggregation function to each item in turn and emitting the result
-
reduce( )
-
-
in the form of a single mutable data structure
-
collect( )
-
-
by applying a function to each item in the sequence, blocking until complete
-
forEach( )
-
- -
I want to convert the entire sequence of items emitted by an Observable
-
-
into a Future
-
toFuture( )
-
into an Iterable
-
toIterable( )
-
-
that returns the most recently item emitted by the Observable
-
mostRecent( )
-
-
only if it has not previously returned that item
-
latest( )
-
-
that returns the next item when it is emitted by the Observable
-
latest( )
-
-
into an Iterator
-
getIterator( ) or toIterator( )
-
into a List
-
toList( )
-
-
sorted by some criterion
-
toSortedList( )
-
-
into a Map
-
toMap( )
-
-
that is also an ArrayList
-
toMultiMap( )
-
-
- -
I want an Observable to emit exactly one item
-
-
so I want it to notify of an error otherwise
-
single( )
-
so I want it to notify of an error if it emits more than one, or a default item if it emits none
-
singleOrDefault( )
-
that matches a predicate
-
-
so I want it to notify of an error otherwise
-
single(predicate)
-
so I want it to notify of an error if it emits more than one, or a default item if it emits none
-
singleorDefault(predicate)
-
-
- -
I want an operator to operate on a particular Scheduler
-
subscribeOn( )
-
-
doing its processing in parallel on multiple threads without making the resulting Observable poorly-behaved
-
parallel( )
-
when it notifies Observers
-
observeOn( )
-
- -
I want an Observable to invoke a particular action
-
-
whenever it emits an item
-
doOnEach(action)
-
when it issues a completed notification
-
doOnCompleted(action)
-
when it issues an error notification
-
doOnError(action)
-
when it issues a completed or error notification
-
doOnTerminate(action)
-
after it has issued a completed or error notification
-
finallyDo(action)
-
whenever it emits an item or issues a completed/error notification
-
doOnEach(observer)
-
- -
I want an Observable that will notify observers of an error
-
error( )
-
-
if a specified period of time elapses without it emitting an item
-
timeout(time,unit)
-
- -
I want an Observable to recover gracefully
-
-
from a timeout by switching to a backup Observable
-
timeout(time,unit,fallback)
-
from an upstream error notification
-
-
by switching to a particular backup Observable
-
onErrorResumeNext(sequence)
-
-
but only if the error is an Exception
-
onExceptionResumeNext( )
-
-
by switching to a backup Observable returned from a function that is passed the error
-
onErrorResumeNext(throwable,function)
-
-
and by then continuing to observe the source Observable in spite of the error termination
-
onErrorFlatMap( )
-
-
by emitting a particular item and completing normally
-
onErrorReturn( )
-
by attempting to resubscribe to the upstream Observable
-
retry( )
-
-
a certain number of times
-
retry(count)
-
so long as a predicate remains true
-
retry(predicate)
-
-
-
from being potentially unserialized or otherwise poorly-behaved
-
serialize( )
-
- -
I want to create a resource that has the same lifespan as the Observable
-
using( )
- -
I want to subscribe to an Observable and receive a Future that blocks until the Observable completes
-
forEachFuture( )
- -
I want an Observable that does not start emitting items to subscribers until asked
-
publish( ) or multicast( )
-
-
and then only emits the last item in its sequence
-
publishLast( )
-
and then emits the complete sequence, even to those who subscribe after the sequence has begun
-
replay( )
-
but I want it to go away once all of its subscribers unsubscribe
-
refCount( ) or share( )
-
and then I want to ask it to start
-
connect( )
-
- -
I want an Observable to retransmit items to observers who subscribe late
-
cache( )
- -
-
-
-

- ⓐ: this operator is part of the optional async-util package
- ⓑ: this operator is part of the BlockingObservable subclass
- ⓒ: this operator is part of the optional computation-expressions package
- ⓜ: this operator is part of the optional math package
- Ⓢ: a variant of this operator allows you to choose a particular Scheduler
-

- I have omitted parameter names from some methods where they are not necessary to distinguish variants of the method. This page was inspired by the RxJS tables (static and instance) created by Paul Taylor. -

- - diff --git a/operators.md b/operators.md deleted file mode 100644 index 357dd9dda9..0000000000 --- a/operators.md +++ /dev/null @@ -1,644 +0,0 @@ - -

- This tree can help you find the Observable operator you’re looking for. -

-
-
-
I want to create a new Observable
-
-
that emits a particular item
-
just( )
-
-
that was returned from a function called at subscribe-time
-
start( )
-
-
anew for each subscriber
-
toAsync( )
-
-
that was returned from an Action called at subscribe-time
-
fromAction( )
-
that was returned from a Callable called at subscribe-time
-
fromCallable( )
-
that was returned from a Runnable called at subscribe-time
-
fromRunnable( )
-
after a specified delay
-
timer( )
-
-
that emits a particular set of 1–10 items
-
from( )
-
that obtains its sequence from an Array or Iterable
-
from( )
-
by retrieving it from a Future
-
deferFuture( )
-
that obtains its sequence from a Future
-
from( )
-
-
with a timeout
-
from( )
-
-
that obtains its sequence from an Action called periodically
-
runAsync( )
-
that emits a sequence of items repeatedly
-
repeat( )
-
-
as long as a predicate remains true
-
whileDo( )
-
-
but at least once, no matter what
-
doWhile( )
-
-
-
from scratch, with custom logic
-
create( )
-
for each observer that subscribes
-
defer( )
-
that emits a sequence of integers
-
range( )
-
-
at particular intervals of time
-
interval( )
-
-
after a specified delay
-
timer( )
-
-
-
that completes without emitting items
-
empty( )
-
that does nothing at all
-
never( )
-
- -
I want to create an Observable by combining other Observables
-
-
and emitting all of the items from all of the Observables in whatever order they are received
-
-
where the source Observables are passed to the operator as parameters
-
merge(…)
-
where the source Observables are found in an Array
-
merge(sequences)
-
where the source Observables are found in an Iterable or Observable
-
merge(sequences)
-
-
but I only want to process a certain number of them at once
-
merge(sequences,maxConcurrent)
-
-
but not forwarding any error notifications until all source Observables have terminated
-
mergeDelayError( )
-
-
and emitting all of the items from all of the Observables, one Observable at a time
-
concat( )
-
by combining the items from two or more Observables sequentially to come up with new items to emit
-
-
whenever each of the Observables has emitted a new item
-
zip( )
-
whenever any of the Observables has emitted a new item
-
combineLatest( )
-
whenever an item is emitted by one Observable in a window defined by an item emitted by another
-
join( )
-
-
based on an Observable that emits all items that have fallen in such a window
-
groupJoin( )
-
-
by means of Pattern and Plan intermediaries
-
and/then/when
-
-
and emitting the items from only the most-recently emitted of those Observables
-
switchOnNext( )
-
and mirroring only one of those Observables (which one depends on a parameter I am passed)
-
switchCase( )
-
reducing an Observable that emits many Observables to one that emits as many Observables as I have processes to process them on
-
parallelMerge(…)
-
- -
I want emit the items from an Observable after transforming them
-
-
one at a time with a function
-
map( )
-
by casting them to a particular type
-
cast( )
-
by emitting all of the items emitted by corresponding Observables
-
flatMap( )
-
-
combined with the original items by means of a function
-
mergeMap(collectionSelector,resultSelector)
-
-
by emitting all of the items in corresponding Iterables
-
mergeMapIterable(collectionSelector)
-
-
combined with the original items by means of a function
-
mergeMapIterable(collectionSelector,resultSelector)
-
-
based on all of the items that preceded them
-
scan( )
-
by combining them sequentially with the items in an Iterable by means of a function
-
zip(iterable,zipFunction)
-
by attaching a timestamp to them
-
timestamp( )
-
into an indicator of the amount of time that lapsed before the emission of the item
-
timeInterval( )
-
- -
I want to shift the items emitted by an Observable forward in time before reemitting them
-
delay(delay,unit)
-
-
with the amount of the shift calculated on a per-item basis
-
delay(itemDelay)
-
-
and the initial subscription to the Observable shifted as well
-
delay(subscriptionDelay,itemDelay)
-
-
- -
I want to transform items and notifications from an Observable into items and reemit them
-
-
by emitting all of the items emitted by corresponding Observables
-
mergeMap( )
-
by wrapping them in Notification objects
-
materialize( )
-
-
which I can then unwrap again with
-
dematerialize( )
-
-
- -
I want to ignore all items emitted by an Observable and only pass along its completed/error notification
-
ignoreElements( )
- -
I want to mirror an Observable but prefix items to its sequence
-
-
obtained from an Array or Iterable
-
startWith(values)
-
obtained from an Observable
-
startWith(values)
-
passed as parameters to the operator
-
startWith(…)
-
only if its sequence is empty
-
defaultIfEmpty(…)
-
- -
I want to collect items from an Observable and reemit them as buffers of items
-
-
with a maximum number of items per buffer
-
buffer(count)
-
-
and starting every n items
-
buffer(count,skip)
-
-
each time a second Observable emits an item
-
buffer(boundary)
-
-
with buffers given an initial capacity for efficiency reasons
-
buffer(boundary,initialCapacity)
-
where that second Observable is returned from a function I supply
-
buffer(bufferClosingSelector)
-
-
and operates on the emission of a third Observable that opens the buffer
-
buffer(bufferOpenings,bufferClosingSelector)
-
-
-
at periodic intervals
-
buffer(timespan,unit)
-
-
or when a certain maximum number of items fill the buffer
-
buffer(timespan,unit,count)
-
for a certain period of time after the interval begins
-
buffer(timespan,timeshift,unit)
-
-
containing only the last items emitted
-
-
that is, the last n items
-
takeLastBuffer(count)
-
-
emitted during a window of time before the Observable completed
-
takeLastBuffer(count,time,unit)
-
-
during a window of time before the Observable completed
-
takeLastBuffer(time,unit)
-
-
- -
I want to split one Observable into multiple Observables
-
-
with a maximum number of items per sub-Observable
-
window(count)
-
-
and starting every n items
-
window(count,skip)
-
-
each time a second Observable emits an item
-
window(boundary)
-
-
where that second Observable is returned from a function I supply
-
window(closingSelector)
-
-
and operates on the emission of a third Observable that starts the sub-Observable
-
window(windowOpenings,closingSelector)
-
-
-
at periodic intervals
-
window(timespan,unit)
-
-
or when a certain maximum number of items have been emitted on the sub-Observable
-
window(timespan,unit,count)
-
for a certain period of time after the interval begins
-
window(timespan,timeshift,unit)
-
-
so that similar items end up on the same Observable
-
groupBy( )
-
-
but periodically completing some of those Observables even if the source is not complete
-
groupByUntil(keySelector,durationSelector)
-
-
and transforming the items before emitting them on those Observables
-
groupByUntil(keySelector,valueSelector,durationSelector)
-
and then collecting similarly grouped Observables back together again
-
pivot( )
-
-
-
- -
I want to retrieve from an Observable
-
-
the last item emitted before it completed
-
last( )
-
-
or a default item if none were emitted
-
lastOrDefault( )
-
that matches a predicate
-
last(predicate)
-
-
or a default item if none did
-
lastOrDefault(predicate)
-
-
-
the sole item it emitted
-
-
or an exception if it did not emit exactly one
-
single( )
-
-
or rather a default item if it did not emit any
-
singleOrDefault( )
-
-
that matches a predicate, or an exception if exactly one did not
-
single(predicate)
-
-
or rather a default item if none did
-
singleOrDefault(predicate)
-
-
-
the first item it emitted
-
first( )
-
-
or a default item if none were emitted
-
firstOrDefault( )
-
that matches a predicate
-
first(predicate)
-
-
or a default item if none did
-
firstOrDefault(predicate)
-
-
-
- -
I want to reemit only certain items from an Observable
-
-
by filtering out those that do not match some predicate
-
filter( )
-
by filtering out those that are not of a particular type
-
ofType( )
-
that is, only the first item
-
-
or notify of an error if the source is empty
-
first( )
-
or a default value if the source is empty
-
firstOrDefault(defaultValue)
-
that matches a predicate
-
takeFirst(predicate)
-
-
or notify of an error if none do
-
first(predicate)
-
or a default value if none do
-
firstOrDefault(defaultValue,predicate)
-
-
-
that is, only the first items
-
-
that is, the first n items
-
take(num)
-
that is, items emitted by the source during an initial period of time
-
take(time,unit)
-
-
that is, only the last item
-
last( )
-
-
that meets some predicate
-
last(predicate)
-
-
or a default item if none do
-
lastOrDefault(predicate)
-
-
or a default item the source emits nothing
-
lastOrDefault( )
-
-
that is, only item n
-
elementAt( )
-
-
or a default value if there is no item n
-
elementAtOrDefault( )
-
-
that is, only those items after the first items
-
-
that is, after the first n items
-
skip(num)
-
-
or until one of those items matches a predicate
-
skipWhileWithIndex( )
-
-
that is, until one of those items matches a predicate
-
skipWhile( )
-
that is, after an initial period of time
-
skip(time,unit)
-
that is, after a second Observable emits an item
-
skipUntil( )
-
-
that is, those items except the last items
-
-
that is, except the last n items
-
skipLast( )
-
-
or until one of those items matches a predicate
-
takeWhileWithIndex( )
-
-
that is, until one of those items matches a predicate
-
takeWhile( )
-
that is, except items emitted during a period of time before the source completes
-
skipLast(time,unit)
-
that is, except items emitted after a second Observable emits an item
-
takeUntil( )
-
-
by sampling the Observable periodically
-
-
based on a timer
-
-
and emitting the most-recently emitted item in the period
-
sample(time,unit)
-
and emitting the first-emitted item in the period
-
throttleFirst(time,unit)
-
-
based on emissions from another Observable
-
-
and emitting the most-recently emitted item in the period
-
sample(sampler)
-
and emitting the first-emitted item in the period
-
throttleFirst(sampler)
-
-
-
by only emitting items that are not followed by other items within some duration
-
-
based on a timer
-
throttleWithTimeout(time,unit)
-
based on emissions from another Observable
-
debounce(debounceSelector)
-
-
by suppressing items that are duplicates of already-emitted items
-
distinct( )
-
-
according to a particular function
-
distinct(keySelector)
-
if they immediately follow the item they are duplicates of
-
distinctUntilChanged( )
-
-
according to a particular function
-
distinctUntilChanged(keySelector)
-
-
-
by delaying my subscription to it for some time after it begins emitting items
-
delaySubscription(delay,unit)
-
- -
I want to reemit items from an Observable only on condition
-
-
that it was the first of a collection of Observables to emit an item
-
amb( )
-
that some predicate is true
-
ifThen( )
-
- -
I want to evaluate the entire sequence of items emitted by an Observable
-
-
and emit a single boolean indicating if all of the items pass some test
-
all( )
-
and emit a single boolean indicating if any of the items pass some test
-
contains( )
-
and emit a single boolean indicating if the Observable emitted any items
-
exists( )
-
and emit a single boolean indicating if the Observable emitted no items
-
isEmpty( )
-
and emit a single boolean indicating if the sequence is identical to one emitted by a second Observable
-
sequenceEqual( )
-
and emit the average of all of their values
-
averageType( )
-
and emit the sum of all of their values
-
sumType( )
-
and emit a number indicating how many items were in the sequence
-
[long]count( )
-
and emit the item with the maximum value
-
max( )
-
-
according to some value-calculating function
-
maxBy( )
-
-
and emit the item with the minimum value
-
min( )
-
-
according to some value-calculating function
-
minBy( )
-
-
by applying an aggregation function to each item in turn and emitting the result
-
reduce( )
-
-
in the form of a single mutable data structure
-
collect( )
-
-
by applying a function to each item in the sequence, blocking until complete
-
forEach( )
-
- -
I want to convert the entire sequence of items emitted by an Observable
-
-
into a Future
-
toFuture( )
-
into an Iterable
-
toIterable( )
-
-
that returns the most recently item emitted by the Observable
-
mostRecent( )
-
-
only if it has not previously returned that item
-
latest( )
-
-
that returns the next item when it is emitted by the Observable
-
latest( )
-
-
into an Iterator
-
getIterator( ) or toIterator( )
-
into a List
-
toList( )
-
-
sorted by some criterion
-
toSortedList( )
-
-
into a Map
-
toMap( )
-
-
that is also an ArrayList
-
toMultiMap( )
-
-
- -
I want an Observable to emit exactly one item
-
-
so I want it to notify of an error otherwise
-
single( )
-
so I want it to notify of an error if it emits more than one, or a default item if it emits none
-
singleOrDefault( )
-
that matches a predicate
-
-
so I want it to notify of an error otherwise
-
single(predicate)
-
so I want it to notify of an error if it emits more than one, or a default item if it emits none
-
singleorDefault(predicate)
-
-
- -
I want an operator to operate on a particular Scheduler
-
subscribeOn( )
-
-
doing its processing in parallel on multiple threads without making the resulting Observable poorly-behaved
-
parallel( )
-
when it notifies Observers
-
observeOn( )
-
- -
I want an Observable to invoke a particular action
-
-
whenever it emits an item
-
doOnEach(action)
-
when it issues a completed notification
-
doOnCompleted(action)
-
when it issues an error notification
-
doOnError(action)
-
when it issues a completed or error notification
-
doOnTerminate(action)
-
after it has issued a completed or error notification
-
finallyDo(action)
-
whenever it emits an item or issues a completed/error notification
-
doOnEach(observer)
-
- -
I want an Observable that will notify observers of an error
-
error( )
-
-
if a specified period of time elapses without it emitting an item
-
timeout(time,unit)
-
- -
I want an Observable to recover gracefully
-
-
from a timeout by switching to a backup Observable
-
timeout(time,unit,fallback)
-
from an upstream error notification
-
-
by switching to a particular backup Observable
-
onErrorResumeNext(sequence)
-
-
but only if the error is an Exception
-
onExceptionResumeNext( )
-
-
by switching to a backup Observable returned from a function that is passed the error
-
onErrorResumeNext(throwable,function)
-
-
and by then continuing to observe the source Observable in spite of the error termination
-
onErrorFlatMap( )
-
-
by emitting a particular item and completing normally
-
onErrorReturn( )
-
by attempting to resubscribe to the upstream Observable
-
retry( )
-
-
a certain number of times
-
retry(count)
-
so long as a predicate remains true
-
retry(predicate)
-
-
-
from being potentially unserialized or otherwise poorly-behaved
-
serialize( )
-
- -
I want to create a resource that has the same lifespan as the Observable
-
using( )
- -
I want to subscribe to an Observable and receive a Future that blocks until the Observable completes
-
forEachFuture( )
- -
I want an Observable that does not start emitting items to subscribers until asked
-
publish( ) or multicast( )
-
-
and then only emits the last item in its sequence
-
publishLast( )
-
and then emits the complete sequence, even to those who subscribe after the sequence has begun
-
replay( )
-
but I want it to go away once all of its subscribers unsubscribe
-
refCount( ) or share( )
-
and then I want to ask it to start
-
connect( )
-
- -
I want an Observable to retransmit items to observers who subscribe late
-
cache( )
- -
-
-
-

- ⓐ: this operator is part of the optional async-util package
- ⓑ: this operator is part of the BlockingObservable subclass
- ⓒ: this operator is part of the optional computation-expressions package
- ⓜ: this operator is part of the optional math package
- Ⓢ: a variant of this operator allows you to choose a particular Scheduler
-

- I have omitted parameter names from some methods where they are not necessary to distinguish variants of the method. This page was inspired by the RxJS tables (static and instance) created by Paul Taylor. -

diff --git a/pmd.xml b/pmd.xml new file mode 100644 index 0000000000..728e1e4d1d --- /dev/null +++ b/pmd.xml @@ -0,0 +1,222 @@ + + + RxJava PMD ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/push_javadoc.sh b/push_javadoc.sh new file mode 100644 index 0000000000..28ce74f1db --- /dev/null +++ b/push_javadoc.sh @@ -0,0 +1,122 @@ +#!/bin/bash +# ---------------------------------------------------------- +# Automatically push back the generated JavaDocs to gh-pages +# ---------------------------------------------------------- +# based on https://gist.github.com/willprice/e07efd73fb7f13f917ea + +# specify the common address for the repository +targetRepo=github.com/ReactiveX/RxJava.git +# ======================================================================= + +# get the current build tag if any +buildTag="$BUILD_TAG" +echo -e "Build tag: '$buildTag'" + +if [ "$buildTag" == "" ]; then + buildTag="snapshot" +else + buildTag="${buildTag:1}" +fi + +echo -e "JavaDocs pushback for tag: $buildTag" + +# check if the token is actually there +if [ "$JAVADOCS_TOKEN" == "" ]; then + echo -e "No access to GitHub, skipping JavaDocs pushback." + exit 0 +fi + +# prepare the git information +git config --global user.email "akarnokd+ci@gmail.com" +git config --global user.name "akarnokd+ci" + +# setup the remote +echo -e "Adding the target repository to git" +git remote add origin-pages https://${JAVADOCS_TOKEN}@${targetRepo} > /dev/null 2>&1 + +# stash changes due to chmod +echo -e "Stashing any local non-ignored changes" +git stash + +# get the gh-pages +echo -e "Update branches and checking out gh-pages" +git fetch --all +git branch -a +git checkout -b gh-pages origin-pages/gh-pages + +# releases should update 2 extra locations +if [ "$buildTag" != "snapshot" ]; then + # for releases, add a new directory with the new version + # and carefully replace the others + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # enable once 3.x is mainstream + # vvvvvvvvvvvvvvvvvvvvvvvvvvvvv + + # 1.) main javadoc + # ---------------- + # remove the io subdir + #echo -e "Removing javadoc/io" + #rm -r javadoc/io + + # remove the html files + #echo -e "Removing javadoc/*.html" + #rm javadoc/*.html + + # copy the new doc + #echo -e "Copying to javadoc/" + #yes | cp -rf ./build/docs/javadoc/ . + + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + # enable once 3.x is mainstream + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + # 2.) 3.x javadoc + # remove the io subdir + echo -e "Removing 3.x/javadoc/io" + rm -r 3.x/javadoc/io + + # remove the html files + echo -e "Removing 3.x/javadoc/*.html" + rm 3.x/javadoc/*.html + + # copy the new doc + echo -e "Copying to 3.x/javadoc/" + yes | cp -rf ./build/docs/javadoc/ 3.x/ +fi + +# 3.) create a version/snapshot specific copy of the docs +# clear the existing tag +echo -e "Removing to 3.x/javadoc/${buildTag}" +rm -r 3.x/javadoc/${buildTag} + +# copy the new doc +echo -e "Copying to 3.x/javadoc/${buildTag}" +yes | cp -rf ./build/docs/javadoc/ 3.x/javadoc/${buildTag}/ + + +# stage all changed and new files +echo -e "Staging new files" +git add *.html +git add *.css +git add *.js +git add *package-list* + +# remove tracked but deleted files +echo -e "Removing deleted files" +git add -u + +# commit all +echo -e "commit CI build: $CI_BUILD_NUMBER for $buildTag" +git commit --message "CI build: $CI_BUILD_NUMBER for $buildTag" + +# debug file list +#find -name "*.html" + +# push it +echo -e "Pushing back changes." +git push --quiet --set-upstream origin-pages gh-pages + + +# we are done +echo -e "JavaDocs pushback complete." \ No newline at end of file diff --git a/rxjava-contrib/rxjava-android-samples-build-wrapper/build.gradle b/rxjava-contrib/rxjava-android-samples-build-wrapper/build.gradle deleted file mode 100644 index 03513b751f..0000000000 --- a/rxjava-contrib/rxjava-android-samples-build-wrapper/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -tasks.build.doLast { - def androidHome = System.getenv("ANDROID_HOME") - if (project.hasProperty('buildAndroidSamples') && !androidHome.isEmpty()) { - println("Android SDK detected at $androidHome, running samples build") - project.exec { - workingDir '../rxjava-android-samples' - - commandLine "./gradlew", "clean", "packageDebug" - } - } -} \ No newline at end of file diff --git a/rxjava-contrib/rxjava-android-samples/.gitignore b/rxjava-contrib/rxjava-android-samples/.gitignore deleted file mode 100644 index d6bfc95b18..0000000000 --- a/rxjava-contrib/rxjava-android-samples/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.gradle -/local.properties -/.idea/workspace.xml -.DS_Store diff --git a/rxjava-contrib/rxjava-android-samples/build.gradle b/rxjava-contrib/rxjava-android-samples/build.gradle deleted file mode 100644 index 47786d4ee6..0000000000 --- a/rxjava-contrib/rxjava-android-samples/build.gradle +++ /dev/null @@ -1,17 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:0.11.+' - } -} - -allprojects { - repositories { - mavenLocal() - mavenCentral() - } -} diff --git a/rxjava-contrib/rxjava-android-samples/gradle.properties b/rxjava-contrib/rxjava-android-samples/gradle.properties deleted file mode 100644 index 5d08ba75bb..0000000000 --- a/rxjava-contrib/rxjava-android-samples/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Settings specified in this file will override any Gradle settings -# configured through the IDE. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file diff --git a/rxjava-contrib/rxjava-android-samples/gradle/wrapper/gradle-wrapper.properties b/rxjava-contrib/rxjava-android-samples/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 0830e23fd9..0000000000 --- a/rxjava-contrib/rxjava-android-samples/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Sat Jun 07 16:12:40 CEST 2014 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip diff --git a/rxjava-contrib/rxjava-android-samples/gradlew b/rxjava-contrib/rxjava-android-samples/gradlew deleted file mode 100755 index 91a7e269e1..0000000000 --- a/rxjava-contrib/rxjava-android-samples/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/rxjava-contrib/rxjava-android-samples/gradlew.bat b/rxjava-contrib/rxjava-android-samples/gradlew.bat deleted file mode 100644 index 8a0b282aa6..0000000000 --- a/rxjava-contrib/rxjava-android-samples/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/rxjava-contrib/rxjava-android-samples/samples/.gitignore b/rxjava-contrib/rxjava-android-samples/samples/.gitignore deleted file mode 100644 index 796b96d1c4..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/rxjava-contrib/rxjava-android-samples/samples/build.gradle b/rxjava-contrib/rxjava-android-samples/samples/build.gradle deleted file mode 100644 index fb177b35d9..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -apply plugin: 'android' - -android { - compileSdkVersion 19 - buildToolsVersion "19.1.0" - - defaultConfig { - minSdkVersion 14 - targetSdkVersion 19 - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - runProguard false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' - } - } -} - -// make sure we always compile against the latest version of RxJava -def rootProjectProperties = new Properties() -file("../../../gradle.properties").withReader { reader -> - rootProjectProperties.load(reader) - properties.putAll(rootProjectProperties) -} - -dependencies { - def rxjVersion = rootProjectProperties.get("version") - compile "com.netflix.rxjava:rxjava-android:$rxjVersion" - compile fileTree(dir: 'libs', include: ['*.jar']) -} diff --git a/rxjava-contrib/rxjava-android-samples/samples/proguard-rules.txt b/rxjava-contrib/rxjava-android-samples/samples/proguard-rules.txt deleted file mode 100644 index c423b03cb2..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/proguard-rules.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /usr/local/opt/android-sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} \ No newline at end of file diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/AndroidManifest.xml b/rxjava-contrib/rxjava-android-samples/samples/src/main/AndroidManifest.xml deleted file mode 100644 index 96b3fcb7d0..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/AndroidManifest.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/ListFragmentActivity.java b/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/ListFragmentActivity.java deleted file mode 100644 index 446bffa55d..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/ListFragmentActivity.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.netflix.rxjava.android.samples; - -import android.app.Activity; -import android.app.ListFragment; -import android.os.Bundle; -import android.widget.ArrayAdapter; - -import rx.Observable; -import rx.Subscriber; - -import static rx.android.schedulers.AndroidSchedulers.mainThread; - -/** - * Problem: - * You have an asynchronous sequence that emits items to be displayed in a list. You want the data - * to survive rotation changes. - *

- * Solution: - * Combine {@link android.app.Fragment#setRetainInstance(boolean)} in a ListFragment with - * {@link rx.android.schedulers.AndroidSchedulers#mainThread()} and an {@link rx.Observable.Operator} - * that binds to the list adapter. - */ -public class ListFragmentActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setTitle("Lists"); - setContentView(R.layout.list_fragment_activity); - } - - @SuppressWarnings("ConstantConditions") - public static class RetainedListFragment extends ListFragment { - - private ArrayAdapter adapter; - - public RetainedListFragment() { - setRetainInstance(true); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - adapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1); - setListAdapter(adapter); - SampleObservables.numberStrings(1, 20, 250) - .observeOn(mainThread()) - .lift(new BindAdapter()) - .subscribe(); - } - - private final class BindAdapter implements Observable.Operator { - @Override - public Subscriber call(Subscriber subscriber) { - return new Subscriber() { - @Override - public void onCompleted() { - adapter.notifyDataSetChanged(); - } - - @Override - public void onError(Throwable throwable) { - - } - - @Override - public void onNext(String strings) { - adapter.add(strings); - } - }; - } - } - } -} diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/ListenInOutActivity.java b/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/ListenInOutActivity.java deleted file mode 100644 index 893124b2f2..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/ListenInOutActivity.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.netflix.rxjava.android.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.ToggleButton; - -import rx.Observable; -import rx.Observer; -import rx.Subscription; -import rx.observables.ConnectableObservable; - -import static rx.android.observables.AndroidObservable.bindActivity; - -/** - * Activity that binds to a counting sequence and is able to listen in and out to that - * sequence by pressing a toggle button. The button disables itself once the sequence - * finishes. - */ -public class ListenInOutActivity extends Activity implements Observer { - - private Observable source; - private Subscription subscription; - private TextView textView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.listen_in_out_activity); - - textView = (TextView) findViewById(android.R.id.text1); - - // in a production app, you would use dependency injection, fragments, or other - // means to preserve the observable, but this will suffice here - source = (Observable) getLastNonConfigurationInstance(); - if (source == null) { - source = SampleObservables.numberStrings(1, 100, 200).publish(); - ((ConnectableObservable) source).connect(); - } - - subscribeToSequence(); - } - - private void subscribeToSequence() { - subscription = bindActivity(this, source).subscribe(this); - } - - @Override - public Object onRetainNonConfigurationInstance() { - return source; - } - - @Override - protected void onDestroy() { - subscription.unsubscribe(); - super.onDestroy(); - } - - @Override - public void onCompleted() { - TextView button = (TextView) findViewById(R.id.toggle_button); - button.setText("Completed"); - button.setEnabled(false); - } - - @Override - public void onError(Throwable e) { - e.printStackTrace(); - Toast.makeText(this, "Error: " + e, Toast.LENGTH_SHORT).show(); - } - - @Override - public void onNext(String s) { - textView.setText(s); - } - - public void onSequenceToggleClicked(View view) { - if (((ToggleButton) view).isChecked()) { - subscription.unsubscribe(); - } else { - subscribeToSequence(); - } - } -} diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/ListeningFragmentActivity.java b/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/ListeningFragmentActivity.java deleted file mode 100644 index 966568fd15..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/ListeningFragmentActivity.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.netflix.rxjava.android.samples; - -import android.app.Activity; -import android.app.Fragment; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import android.widget.Toast; - -import rx.Subscriber; -import rx.Subscription; -import rx.observables.ConnectableObservable; -import rx.subscriptions.Subscriptions; - -import static rx.android.observables.AndroidObservable.bindFragment; - -/** - * Problem: - * You have a background sequence which keeps emitting items (either a limited or unlimited number) - * and your UI component should be able to "listen in" to the sequence, i.e. it's okay to miss - * in-flight items when going e.g. through a screen rotation or being otherwise detached from the - * screen for a limited period of time. (Another example is a "page out" in a fragment ViewPager.) - *

- * This is useful if you need behavior that mimics event buses. Think of a publishing - * Observable as a channel or queue on an event bus. - *

- * Solution: - * Combine {@link android.app.Fragment#setRetainInstance(boolean)} with - * {@link rx.android.schedulers.AndroidSchedulers#mainThread()} and {@link rx.Observable#publish()} - */ -public class ListeningFragmentActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.listening_fragment_activity); - } - - @SuppressWarnings("ConstantConditions") - public static class ListeningFragment extends Fragment { - - private ConnectableObservable strings; - private Subscription subscription = Subscriptions.empty(); - - public ListeningFragment() { - setRetainInstance(true); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - strings = SampleObservables.numberStrings(1, 50, 250).publish(); - strings.connect(); // trigger the sequence - } - - @Override - public void onDestroyView() { - subscription.unsubscribe(); // stop listening - super.onDestroyView(); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.retained_fragment, container, false); - } - - @Override - public void onViewCreated(final View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - final TextView textView = (TextView) view.findViewById(android.R.id.text1); - - // re-connect to sequence - subscription = bindFragment(this, strings).subscribe(new Subscriber() { - - @Override - public void onCompleted() { - Toast.makeText(getActivity(), "Done!", Toast.LENGTH_SHORT).show(); - } - - @Override - public void onError(Throwable throwable) { - - } - - @Override - public void onNext(String s) { - textView.setText(s); - } - }); - } - } -} diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/RetainedFragmentActivity.java b/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/RetainedFragmentActivity.java deleted file mode 100644 index b598fe5c72..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/RetainedFragmentActivity.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.netflix.rxjava.android.samples; - -import android.app.Activity; -import android.app.Fragment; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.widget.TextView; - -import org.json.JSONException; -import org.json.JSONObject; - -import rx.Observable; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Func1; -import rx.subscriptions.Subscriptions; - -import static rx.android.observables.AndroidObservable.bindFragment; - -/** - * Problem: - * You have a data source (where that data is potentially expensive to obtain), and you want to - * emit this data into a fragment. However, you want to gracefully deal with rotation changes and - * not lose any data already emitted. - *

- * Solution: - * Combine {@link android.app.Fragment#setRetainInstance(boolean)} with - * {@link rx.android.schedulers.AndroidSchedulers#mainThread()} and {@link rx.Observable#cache()} - */ -public class RetainedFragmentActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - setTitle("Fake API call"); - setContentView(R.layout.retained_fragment_activity); - } - - @SuppressWarnings("ConstantConditions") - public static class RetainedFragment extends Fragment { - - // in a production app, you don't want to have JSON parser code in your fragment, - // but we'll simplify a little here - private static final Func1 PARSE_JSON = new Func1() { - @Override - public String call(String json) { - try { - JSONObject jsonObject = new JSONObject(json); - return String.valueOf(jsonObject.getInt("result")); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - }; - - private Observable strings; - private Subscription subscription = Subscriptions.empty(); - - public RetainedFragment() { - setRetainInstance(true); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // simulate fetching a JSON document with a latency of 2 seconds - // in retained fragments, it's sufficient to bind the fragment in onCreate, since - // Android takes care of detaching the Activity for us, and holding a reference for - // the duration of the observable does not harm. - strings = bindFragment(this, SampleObservables.fakeApiCall(2000).map(PARSE_JSON).cache()); - } - - @Override - public void onDestroyView() { - subscription.unsubscribe(); - super.onDestroyView(); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - getActivity().setProgressBarIndeterminateVisibility(true); - return inflater.inflate(R.layout.retained_fragment, container, false); - } - - @Override - public void onViewCreated(final View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - final TextView textView = (TextView) view.findViewById(android.R.id.text1); - - // (re-)subscribe to the sequence, which either emits the cached result or simply re- - // attaches the subscriber to wait for it to arrive - subscription = strings.subscribe(new Action1() { - @Override - public void call(String result) { - textView.setText(result); - getActivity().setProgressBarIndeterminateVisibility(false); - } - }); - } - } -} diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/SampleObservables.java b/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/SampleObservables.java deleted file mode 100644 index 7a17bedad5..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/SampleObservables.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.netflix.rxjava.android.samples; - -import android.os.SystemClock; - -import rx.Observable; -import rx.Subscriber; -import rx.functions.Action1; -import rx.functions.Func1; -import rx.schedulers.Schedulers; - -public class SampleObservables { - - /** - * Emits numbers as strings, where these numbers a generated on a background thread. - */ - public static Observable numberStrings(int from, int to, final long delay) { - return Observable.range(from, to).map(new Func1() { - @Override - public String call(Integer integer) { - return integer.toString(); - } - }).doOnNext(new Action1() { - @Override - public void call(String s) { - SystemClock.sleep(delay); - } - }).subscribeOn(Schedulers.newThread()); - } - - public static Observable fakeApiCall(final long delay) { - return Observable.create(new Observable.OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - // simulate I/O latency - SystemClock.sleep(delay); - final String fakeJson = "{\"result\": 42}"; - subscriber.onNext(fakeJson); - subscriber.onCompleted(); - } - }).subscribeOn(Schedulers.io()); - } -} diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/SamplesApplication.java b/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/SamplesApplication.java deleted file mode 100644 index e91e7d543e..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/SamplesApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.netflix.rxjava.android.samples; - -import android.app.Application; -import android.os.StrictMode; - -public class SamplesApplication extends Application { - - @Override - public void onCreate() { - super.onCreate(); - StrictMode.enableDefaults(); - } -} diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/UIBindingActivity.java b/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/UIBindingActivity.java deleted file mode 100644 index 8d4508846c..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/java/com/netflix/rxjava/android/samples/UIBindingActivity.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.netflix.rxjava.android.samples; - -import android.app.Activity; -import android.app.Fragment; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.widget.Button; -import android.widget.TextView; -import org.json.JSONException; -import org.json.JSONObject; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.functions.Action1; -import rx.functions.Func1; -import rx.subscriptions.Subscriptions; - -/** - * Problem: - * You have a data source (where that data is potentially expensive to obtain), and you want to - * emit this data into a fragment. However, you want to gracefully deal with rotation changes and - * not lose any data already emitted. - *

- * You also want your UI to update accordingly to the data being emitted. - * - * @author zsiegel (zsiegel87@gmail.com) - */ -public class UIBindingActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - setTitle("UIBinding"); - setContentView(R.layout.ui_binding_activity); - } - - @SuppressWarnings("ConstantConditions") - public static class RetainedBindingFragment extends Fragment { - - private Button startButton; - - private Observable request; - private Subscription requestSubscription = Subscriptions.empty(); - - // in a production app, you don't want to have JSON parser code in your fragment, - // but we'll simplify a little here - private static final Func1 PARSE_JSON = new Func1() { - @Override - public String call(String json) { - try { - JSONObject jsonObject = new JSONObject(json); - return String.valueOf(jsonObject.getInt("result")); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - }; - - public RetainedBindingFragment() { - setRetainInstance(true); - } - - /** - * We un-subscribe whenever we are paused - */ - @Override - public void onPause() { - requestSubscription.unsubscribe(); - super.onPause(); - } - - /** - * We re-subscribe whenever we are resumed - */ - @Override - public void onResume() { - super.onResume(); - subscribe(); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.retained_fragment_v2, container, false); - } - - @Override - public void onViewCreated(final View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - final TextView textView = (TextView) getView().findViewById(android.R.id.text1); - - startButton = (Button) view.findViewById(R.id.button); - startButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - textView.setText(""); - start(); - } - }); - } - - private void start() { - - request = SampleObservables - .fakeApiCall(5000) - .map(PARSE_JSON) - .observeOn(AndroidSchedulers.mainThread()) - .cache(); - - subscribe(); - } - - /** - * We subscribe/re-subscribe here - */ - private void subscribe() { - if (request != null) { - - final TextView textView = (TextView) getView().findViewById(android.R.id.text1); - - requestSubscription = request.map(new Func1() { - - //Consume the data that comes back and then signal that we are done - @Override - public Boolean call(String s) { - textView.setText(s); - return true; - } - }).startWith(false) //Before we receive data our request is not complete - .subscribe(new Action1() { - - //We update the UI based on the state of the request - @Override - public void call(Boolean completed) { - setRequestInProgress(completed); - } - }); - } - } - - private void setRequestInProgress(boolean completed) { - getActivity().setProgressBarIndeterminateVisibility(!completed); - startButton.setEnabled(completed); - } - } -} diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-hdpi/ic_launcher.png b/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 96a442e5b8..0000000000 Binary files a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-mdpi/ic_launcher.png b/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 359047dfa4..0000000000 Binary files a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-xhdpi/ic_launcher.png b/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index 71c6d760f0..0000000000 Binary files a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-xxhdpi/ic_launcher.png b/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-xxhdpi/ic_launcher.png deleted file mode 100644 index 4df1894644..0000000000 Binary files a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/list_fragment_activity.xml b/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/list_fragment_activity.xml deleted file mode 100644 index f741ee62dc..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/list_fragment_activity.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/listen_in_out_activity.xml b/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/listen_in_out_activity.xml deleted file mode 100644 index bc2c1e1a99..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/listen_in_out_activity.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/listening_fragment_activity.xml b/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/listening_fragment_activity.xml deleted file mode 100644 index ecfc325d20..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/listening_fragment_activity.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/retained_fragment.xml b/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/retained_fragment.xml deleted file mode 100644 index 0fc012b481..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/retained_fragment.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/retained_fragment_activity.xml b/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/retained_fragment_activity.xml deleted file mode 100644 index e5eb51fb86..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/retained_fragment_activity.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/retained_fragment_v2.xml b/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/retained_fragment_v2.xml deleted file mode 100644 index aa5e2792c7..0000000000 --- a/rxjava-contrib/rxjava-android-samples/samples/src/main/res/layout/retained_fragment_v2.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - -